backports 2.3.0 → 2.4.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.
Files changed (55) hide show
  1. data/.irbrc +1 -0
  2. data/README.rdoc +55 -3
  3. data/Rakefile +1 -0
  4. data/VERSION.yml +1 -1
  5. data/backports.gemspec +99 -118
  6. data/lib/backports/1.8.7/string.rb +1 -1
  7. data/lib/backports/1.9.1/array.rb +1 -2
  8. data/lib/backports/1.9.1/file.rb +20 -0
  9. data/lib/backports/1.9.1/float.rb +19 -0
  10. data/lib/backports/1.9.1/hash.rb +20 -3
  11. data/lib/backports/1.9.1/integer.rb +19 -0
  12. data/lib/backports/1.9.1/io.rb +18 -3
  13. data/lib/backports/1.9.1/numeric.rb +9 -0
  14. data/lib/backports/1.9.1/regexp.rb +1 -6
  15. data/lib/backports/1.9.1/stdlib/prime.rb +495 -0
  16. data/lib/backports/1.9.1/stdlib.rb +1 -0
  17. data/lib/backports/1.9.1/string.rb +2 -7
  18. data/lib/backports/1.9.2/array.rb +3 -4
  19. data/lib/backports/1.9.2/complex.rb +6 -0
  20. data/lib/backports/1.9.2/stdlib/matrix/eigenvalue_decomposition.rb +886 -0
  21. data/lib/backports/1.9.2/stdlib/matrix/lup_decomposition.rb +218 -0
  22. data/lib/backports/1.9.2/stdlib/matrix.rb +1872 -0
  23. data/lib/backports/1.9.2/stdlib/set.rb +13 -0
  24. data/lib/backports/1.9.2/stdlib.rb +1 -0
  25. data/lib/backports/1.9.3/io.rb +12 -0
  26. data/lib/backports/1.9.3.rb +5 -0
  27. data/lib/backports/1.9.rb +1 -1
  28. data/lib/backports/basic_object.rb +3 -2
  29. data/lib/backports/force/array_map.rb +1 -0
  30. data/lib/backports/force/enumerable_map.rb +3 -0
  31. data/lib/backports/force/hash_select.rb +9 -0
  32. data/lib/backports/force/string_length.rb +10 -0
  33. data/lib/backports/force/string_size.rb +1 -0
  34. data/lib/backports/tools.rb +137 -1
  35. data/test/README +13 -0
  36. metadata +25 -42
  37. data/.gitignore +0 -7
  38. data/test/_README +0 -1
  39. data/test/array_test.rb +0 -82
  40. data/test/basic_object_test.rb +0 -70
  41. data/test/binding_test.rb +0 -20
  42. data/test/enumerable_test.rb +0 -244
  43. data/test/enumerator_test.rb +0 -45
  44. data/test/hash_test.rb +0 -26
  45. data/test/kernel_test.rb +0 -31
  46. data/test/math_test.rb +0 -59
  47. data/test/method_missing_test.rb +0 -37
  48. data/test/method_test.rb +0 -73
  49. data/test/module_test.rb +0 -20
  50. data/test/object_test.rb +0 -35
  51. data/test/proc_test.rb +0 -116
  52. data/test/regexp_test.rb +0 -14
  53. data/test/string_test.rb +0 -74
  54. data/test/symbol_test.rb +0 -23
  55. data/test/test_helper.rb +0 -8
@@ -0,0 +1,1872 @@
1
+ # encoding: utf-8
2
+ #
3
+ # = matrix.rb
4
+ #
5
+ # An implementation of Matrix and Vector classes.
6
+ #
7
+ # See classes Matrix and Vector for documentation.
8
+ #
9
+ # Current Maintainer:: Marc-André Lafortune
10
+ # Original Author:: Keiju ISHITSUKA
11
+ # Original Documentation:: Gavin Sinclair (sourced from <i>Ruby in a Nutshell</i> (Matsumoto, O'Reilly))
12
+ ##
13
+
14
+ require "e2mmap.rb"
15
+
16
+ module ExceptionForMatrix # :nodoc:
17
+ extend Exception2MessageMapper
18
+ def_e2message(TypeError, "wrong argument type %s (expected %s)")
19
+ def_e2message(ArgumentError, "Wrong # of arguments(%d for %d)")
20
+
21
+ def_exception("ErrDimensionMismatch", "\#{self.name} dimension mismatch")
22
+ def_exception("ErrNotRegular", "Not Regular Matrix")
23
+ def_exception("ErrOperationNotDefined", "Operation(%s) can\\'t be defined: %s op %s")
24
+ def_exception("ErrOperationNotImplemented", "Sorry, Operation(%s) not implemented: %s op %s")
25
+ end
26
+
27
+ #
28
+ # The +Matrix+ class represents a mathematical matrix. It provides methods for creating
29
+ # matrices, operating on them arithmetically and algebraically,
30
+ # and determining their mathematical properties (trace, rank, inverse, determinant).
31
+ #
32
+ # == Method Catalogue
33
+ #
34
+ # To create a matrix:
35
+ # * Matrix[*rows]
36
+ # * Matrix.[](*rows)
37
+ # * Matrix.rows(rows, copy = true)
38
+ # * Matrix.columns(columns)
39
+ # * Matrix.build(row_size, column_size, &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
+ #
49
+ # To access Matrix elements/columns/rows/submatrices/properties:
50
+ # * #[](i, j)
51
+ # * #row_size
52
+ # * #column_size
53
+ # * #row(i)
54
+ # * #column(j)
55
+ # * #collect
56
+ # * #map
57
+ # * #each
58
+ # * #each_with_index
59
+ # * #find_index
60
+ # * #minor(*param)
61
+ #
62
+ # Properties of a matrix:
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?
78
+ #
79
+ # Matrix arithmetic:
80
+ # * #*(m)
81
+ # * #+(m)
82
+ # * #-(m)
83
+ # * #/(m)
84
+ # * #inverse
85
+ # * #inv
86
+ # * #**
87
+ #
88
+ # Matrix functions:
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
103
+ #
104
+ # Complex arithmetic:
105
+ # * conj
106
+ # * conjugate
107
+ # * imag
108
+ # * imaginary
109
+ # * real
110
+ # * rect
111
+ # * rectangular
112
+ #
113
+ # Conversion to other data types:
114
+ # * #coerce(other)
115
+ # * #row_vectors
116
+ # * #column_vectors
117
+ # * #to_a
118
+ #
119
+ # String representations:
120
+ # * #to_s
121
+ # * #inspect
122
+ #
123
+ class Matrix
124
+ include Enumerable
125
+ include ExceptionForMatrix
126
+ unless autoload?(:EigenvalueDecomposition)
127
+ autoload :EigenvalueDecomposition, File.expand_path("../matrix/eigenvalue_decomposition", __FILE__) if 42.respond_to?(:conj)
128
+ autoload :LUPDecomposition, File.expand_path("../matrix/lup_decomposition", __FILE__)
129
+ end
130
+
131
+ # instance creations
132
+ private_class_method :new
133
+ attr_reader :rows
134
+ protected :rows
135
+
136
+ #
137
+ # Creates a matrix where each argument is a row.
138
+ # Matrix[ [25, 93], [-1, 66] ]
139
+ # => 25 93
140
+ # -1 66
141
+ #
142
+ def Matrix.[](*rows)
143
+ rows(rows, false)
144
+ end
145
+
146
+ #
147
+ # Creates a matrix where +rows+ is an array of arrays, each of which is a row
148
+ # of the matrix. If the optional argument +copy+ is false, use the given
149
+ # arrays as the internal structure of the matrix without copying.
150
+ # Matrix.rows([[25, 93], [-1, 66]])
151
+ # => 25 93
152
+ # -1 66
153
+ #
154
+ def Matrix.rows(rows, copy = true)
155
+ rows = convert_to_array(rows)
156
+ rows.map! do |row|
157
+ convert_to_array(row, copy)
158
+ end
159
+ size = (rows[0] || []).size
160
+ rows.each do |row|
161
+ Matrix.Raise ErrDimensionMismatch, "row size differs (#{row.size} should be #{size})" unless row.size == size
162
+ end
163
+ new rows, size
164
+ end
165
+
166
+ #
167
+ # Creates a matrix using +columns+ as an array of column vectors.
168
+ # Matrix.columns([[25, 93], [-1, 66]])
169
+ # => 25 -1
170
+ # 93 66
171
+ #
172
+ def Matrix.columns(columns)
173
+ rows(columns, false).transpose
174
+ end
175
+
176
+ #
177
+ # Creates a matrix of size +row_size+ x +column_size+.
178
+ # It fills the values by calling the given block,
179
+ # passing the current row and column.
180
+ # Returns an enumerator if no block is given.
181
+ #
182
+ # m = Matrix.build(2, 4) {|row, col| col - row }
183
+ # => Matrix[[0, 1, 2, 3], [-1, 0, 1, 2]]
184
+ # m = Matrix.build(3) { rand }
185
+ # => a 3x3 matrix with random elements
186
+ #
187
+ def Matrix.build(row_size, column_size = row_size)
188
+ row_size = CoercionHelper.coerce_to_int(row_size)
189
+ column_size = CoercionHelper.coerce_to_int(column_size)
190
+ raise ArgumentError if row_size < 0 || column_size < 0
191
+ return to_enum :build, row_size, column_size unless block_given?
192
+ rows = Array.new(row_size) do |i|
193
+ Array.new(column_size) do |j|
194
+ yield i, j
195
+ end
196
+ end
197
+ new rows, column_size
198
+ end
199
+
200
+ #
201
+ # Creates a matrix where the diagonal elements are composed of +values+.
202
+ # Matrix.diagonal(9, 5, -3)
203
+ # => 9 0 0
204
+ # 0 5 0
205
+ # 0 0 -3
206
+ #
207
+ def Matrix.diagonal(*values)
208
+ size = values.size
209
+ rows = Array.new(size) {|j|
210
+ row = Array.new(size, 0)
211
+ row[j] = values[j]
212
+ row
213
+ }
214
+ new rows
215
+ end
216
+
217
+ #
218
+ # Creates an +n+ by +n+ diagonal matrix where each diagonal element is
219
+ # +value+.
220
+ # Matrix.scalar(2, 5)
221
+ # => 5 0
222
+ # 0 5
223
+ #
224
+ def Matrix.scalar(n, value)
225
+ diagonal(*Array.new(n, value))
226
+ end
227
+
228
+ #
229
+ # Creates an +n+ by +n+ identity matrix.
230
+ # Matrix.identity(2)
231
+ # => 1 0
232
+ # 0 1
233
+ #
234
+ def Matrix.identity(n)
235
+ scalar(n, 1)
236
+ end
237
+ class << Matrix
238
+ alias unit identity
239
+ alias I identity
240
+ end
241
+
242
+ #
243
+ # Creates a zero matrix.
244
+ # Matrix.zero(2)
245
+ # => 0 0
246
+ # 0 0
247
+ #
248
+ def Matrix.zero(row_size, column_size = row_size)
249
+ rows = Array.new(row_size){Array.new(column_size, 0)}
250
+ new rows, column_size
251
+ end
252
+
253
+ #
254
+ # Creates a single-row matrix where the values of that row are as given in
255
+ # +row+.
256
+ # Matrix.row_vector([4,5,6])
257
+ # => 4 5 6
258
+ #
259
+ def Matrix.row_vector(row)
260
+ row = convert_to_array(row)
261
+ new [row]
262
+ end
263
+
264
+ #
265
+ # Creates a single-column matrix where the values of that column are as given
266
+ # in +column+.
267
+ # Matrix.column_vector([4,5,6])
268
+ # => 4
269
+ # 5
270
+ # 6
271
+ #
272
+ def Matrix.column_vector(column)
273
+ column = convert_to_array(column)
274
+ new [column].transpose, 1
275
+ end
276
+
277
+ #
278
+ # Creates a empty matrix of +row_size+ x +column_size+.
279
+ # At least one of +row_size+ or +column_size+ must be 0.
280
+ #
281
+ # m = Matrix.empty(2, 0)
282
+ # m == Matrix[ [], [] ]
283
+ # => true
284
+ # n = Matrix.empty(0, 3)
285
+ # n == Matrix.columns([ [], [], [] ])
286
+ # => true
287
+ # m * n
288
+ # => Matrix[[0, 0, 0], [0, 0, 0]]
289
+ #
290
+ def Matrix.empty(row_size = 0, column_size = 0)
291
+ Matrix.Raise ArgumentError, "One size must be 0" if column_size != 0 && row_size != 0
292
+ Matrix.Raise ArgumentError, "Negative size" if column_size < 0 || row_size < 0
293
+
294
+ new([[]]*row_size, column_size)
295
+ end
296
+
297
+ #
298
+ # Matrix.new is private; use Matrix.rows, columns, [], etc... to create.
299
+ #
300
+ def initialize(rows, column_size = rows[0].size)
301
+ # No checking is done at this point. rows must be an Array of Arrays.
302
+ # column_size must be the size of the first row, if there is one,
303
+ # otherwise it *must* be specified and can be any integer >= 0
304
+ @rows = rows
305
+ @column_size = column_size
306
+ end
307
+
308
+ def new_matrix(rows, column_size = rows[0].size) # :nodoc:
309
+ self.class.send(:new, rows, column_size) # bypass privacy of Matrix.new
310
+ end
311
+ private :new_matrix
312
+
313
+ #
314
+ # Returns element (+i+,+j+) of the matrix. That is: row +i+, column +j+.
315
+ #
316
+ def [](i, j)
317
+ @rows.fetch(i){return nil}[j]
318
+ end
319
+ alias element []
320
+ alias component []
321
+
322
+ def []=(i, j, v)
323
+ @rows[i][j] = v
324
+ end
325
+ alias set_element []=
326
+ alias set_component []=
327
+ private :[]=, :set_element, :set_component
328
+
329
+ #
330
+ # Returns the number of rows.
331
+ #
332
+ def row_size
333
+ @rows.size
334
+ end
335
+
336
+ #
337
+ # Returns the number of columns.
338
+ #
339
+ attr_reader :column_size
340
+
341
+ #
342
+ # Returns row vector number +i+ of the matrix as a Vector (starting at 0 like
343
+ # an array). When a block is given, the elements of that vector are iterated.
344
+ #
345
+ def row(i, &block) # :yield: e
346
+ if block_given?
347
+ @rows.fetch(i){return self}.each(&block)
348
+ self
349
+ else
350
+ Vector.elements(@rows.fetch(i){return nil})
351
+ end
352
+ end
353
+
354
+ #
355
+ # Returns column vector number +j+ of the matrix as a Vector (starting at 0
356
+ # like an array). When a block is given, the elements of that vector are
357
+ # iterated.
358
+ #
359
+ def column(j) # :yield: e
360
+ if block_given?
361
+ return self if j >= column_size || j < -column_size
362
+ row_size.times do |i|
363
+ yield @rows[i][j]
364
+ end
365
+ self
366
+ else
367
+ return nil if j >= column_size || j < -column_size
368
+ col = Array.new(row_size) {|i|
369
+ @rows[i][j]
370
+ }
371
+ Vector.elements(col, false)
372
+ end
373
+ end
374
+
375
+ #
376
+ # Returns a matrix that is the result of iteration of the given block over all
377
+ # elements of the matrix.
378
+ # Matrix[ [1,2], [3,4] ].collect { |e| e**2 }
379
+ # => 1 4
380
+ # 9 16
381
+ #
382
+ def collect(&block) # :yield: e
383
+ return to_enum(:collect) unless block_given?
384
+ rows = @rows.collect{|row| row.collect(&block)}
385
+ new_matrix rows, column_size
386
+ end
387
+ alias map collect
388
+
389
+ #
390
+ # Yields all elements of the matrix, starting with those of the first row,
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
+ #
401
+ # Matrix[ [1,2], [3,4] ].each { |e| puts e }
402
+ # # => prints the numbers 1 to 4
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_size - 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_size.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_size].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
+ Matrix.Raise ArgumentError, "expected #{which.inspect} to be one of :all, :diagonal, :off_diagonal, :lower, :strict_lower, :strict_upper or :upper"
450
+ end
451
+ self
452
+ end
453
+
454
+ #
455
+ # Same as #each, but the row index and column index in addition to the element
456
+ #
457
+ # Matrix[ [1,2], [3,4] ].each_with_index do |e, row, col|
458
+ # puts "#{e} at #{row}, #{col}"
459
+ # end
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_size - 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
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_size.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_size].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
+ Matrix.Raise ArgumentError, "expected #{which.inspect} to be one of :all, :diagonal, :off_diagonal, :lower, :strict_lower, :strict_upper or :upper"
512
+ end
513
+ self
514
+ end
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
546
+ #
547
+ # Returns a section of the matrix. The parameters are either:
548
+ # * start_row, nrows, start_col, ncols; OR
549
+ # * row_range, col_range
550
+ #
551
+ # Matrix.diagonal(9, 5, -3).minor(0..1, 0..2)
552
+ # => 9 0 0
553
+ # 0 5 0
554
+ #
555
+ # Like Array#[], negative indices count backward from the end of the
556
+ # row or column (-1 is the last element). Returns nil if the starting
557
+ # row or column is greater than row_size or column_size respectively.
558
+ #
559
+ def minor(*param)
560
+ case param.size
561
+ when 2
562
+ row_range, col_range = param
563
+ from_row = row_range.first
564
+ from_row += row_size if from_row < 0
565
+ to_row = row_range.end
566
+ to_row += row_size if to_row < 0
567
+ to_row += 1 unless row_range.exclude_end?
568
+ size_row = to_row - from_row
569
+
570
+ from_col = col_range.first
571
+ from_col += column_size if from_col < 0
572
+ to_col = col_range.end
573
+ to_col += column_size if to_col < 0
574
+ to_col += 1 unless col_range.exclude_end?
575
+ size_col = to_col - from_col
576
+ when 4
577
+ from_row, size_row, from_col, size_col = param
578
+ return nil if size_row < 0 || size_col < 0
579
+ from_row += row_size if from_row < 0
580
+ from_col += column_size if from_col < 0
581
+ else
582
+ Matrix.Raise ArgumentError, param.inspect
583
+ end
584
+
585
+ return nil if from_row > row_size || from_col > column_size || from_row < 0 || from_col < 0
586
+ rows = @rows[from_row, size_row].collect{|row|
587
+ row[from_col, size_col]
588
+ }
589
+ new_matrix rows, [column_size - from_col, size_col].min
590
+ end
591
+
592
+ #--
593
+ # TESTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
594
+ #++
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
+
605
+ #
606
+ # Returns +true+ if this is an empty matrix, i.e. if the number of rows
607
+ # or the number of columns is 0.
608
+ #
609
+ def empty?
610
+ column_size == 0 || row_size == 0
611
+ end
612
+
613
+
614
+ #
615
+ # Returns +true+ is this is an hermitian matrix.
616
+ # Raises an error if matrix is not square.
617
+ #
618
+
619
+ def hermitian?
620
+ Matrix.Raise ErrDimensionMismatch unless square?
621
+ each_with_index(:strict_upper).all? do |e, row, col|
622
+ e == rows[col][row].conj
623
+ end
624
+ end if 42.respond_to?(:conj)
625
+
626
+ #
627
+ # Returns +true+ is this is a lower triangular matrix.
628
+ #
629
+ def lower_triangular?
630
+ each(:strict_upper).all?(&:zero?)
631
+ end
632
+
633
+ #
634
+ # Returns +true+ is this is a normal matrix.
635
+ # Raises an error if matrix is not square.
636
+ #
637
+ def normal?
638
+ Matrix.Raise ErrDimensionMismatch unless square?
639
+ rows.each_with_index do |row_i, i|
640
+ rows.each_with_index do |row_j, j|
641
+ s = 0
642
+ rows.each_with_index do |row_k, k|
643
+ s += row_i[k] * row_j[k].conj - row_k[i].conj * row_k[j]
644
+ end
645
+ return false unless s == 0
646
+ end
647
+ end
648
+ true
649
+ end if 42.respond_to?(:conj)
650
+
651
+ #
652
+ # Returns +true+ is this is an orthogonal matrix
653
+ # Raises an error if matrix is not square.
654
+ #
655
+ def orthogonal?
656
+ Matrix.Raise ErrDimensionMismatch unless square?
657
+ rows.each_with_index do |row, i|
658
+ column_size.times do |j|
659
+ s = 0
660
+ row_size.times do |k|
661
+ s += row[k] * rows[k][j]
662
+ end
663
+ return false unless s == (i == j ? 1 : 0)
664
+ end
665
+ end
666
+ true
667
+ end
668
+
669
+ #
670
+ # Returns +true+ is this is a permutation matrix
671
+ # Raises an error if matrix is not square.
672
+ #
673
+ def permutation?
674
+ Matrix.Raise ErrDimensionMismatch unless square?
675
+ cols = Array.new(column_size)
676
+ rows.each_with_index do |row, i|
677
+ found = false
678
+ row.each_with_index do |e, j|
679
+ if e == 1
680
+ return false if found || cols[j]
681
+ found = cols[j] = true
682
+ elsif e != 0
683
+ return false
684
+ end
685
+ end
686
+ return false unless found
687
+ end
688
+ true
689
+ end
690
+
691
+ #
692
+ # Returns +true+ if all entries of the matrix are real.
693
+ #
694
+ def real?
695
+ all?(&:real?)
696
+ end
697
+
698
+ #
699
+ # Returns +true+ if this is a regular (i.e. non-singular) matrix.
700
+ #
701
+ def regular?
702
+ not singular?
703
+ end
704
+
705
+ #
706
+ # Returns +true+ is this is a singular matrix.
707
+ #
708
+ def singular?
709
+ determinant == 0
710
+ end
711
+
712
+ #
713
+ # Returns +true+ is this is a square matrix.
714
+ #
715
+ def square?
716
+ column_size == row_size
717
+ end
718
+
719
+ #
720
+ # Returns +true+ is this is a symmetric matrix.
721
+ # Raises an error if matrix is not square.
722
+ #
723
+ def symmetric?
724
+ Matrix.Raise ErrDimensionMismatch unless square?
725
+ each_with_index(:strict_upper) do |e, row, col|
726
+ return false if e != rows[col][row]
727
+ end
728
+ true
729
+ end
730
+
731
+ #
732
+ # Returns +true+ is this is a unitary matrix
733
+ # Raises an error if matrix is not square.
734
+ #
735
+ def unitary?
736
+ Matrix.Raise ErrDimensionMismatch unless square?
737
+ rows.each_with_index do |row, i|
738
+ column_size.times do |j|
739
+ s = 0
740
+ row_size.times do |k|
741
+ s += row[k].conj * rows[k][j]
742
+ end
743
+ return false unless s == (i == j ? 1 : 0)
744
+ end
745
+ end
746
+ true
747
+ end if 42.respond_to?(:conj)
748
+
749
+ #
750
+ # Returns +true+ is this is an upper triangular matrix.
751
+ #
752
+ def upper_triangular?
753
+ each(:strict_lower).all?(&:zero?)
754
+ end
755
+
756
+ #
757
+ # Returns +true+ is this is a matrix with only zero elements
758
+ #
759
+ def zero?
760
+ all?(&:zero?)
761
+ end
762
+
763
+ #--
764
+ # OBJECT METHODS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
765
+ #++
766
+
767
+ #
768
+ # Returns +true+ if and only if the two matrices contain equal elements.
769
+ #
770
+ def ==(other)
771
+ return false unless Matrix === other &&
772
+ column_size == other.column_size # necessary for empty matrices
773
+ rows == other.rows
774
+ end
775
+
776
+ def eql?(other)
777
+ return false unless Matrix === other &&
778
+ column_size == other.column_size # necessary for empty matrices
779
+ rows.eql? other.rows
780
+ end
781
+
782
+ #
783
+ # Returns a clone of the matrix, so that the contents of each do not reference
784
+ # identical objects.
785
+ # There should be no good reason to do this since Matrices are immutable.
786
+ #
787
+ def clone
788
+ new_matrix @rows.map(&:dup), column_size
789
+ end
790
+
791
+ #
792
+ # Returns a hash-code for the matrix.
793
+ #
794
+ def hash
795
+ @rows.hash
796
+ end
797
+
798
+ #--
799
+ # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
800
+ #++
801
+
802
+ #
803
+ # Matrix multiplication.
804
+ # Matrix[[2,4], [6,8]] * Matrix.identity(2)
805
+ # => 2 4
806
+ # 6 8
807
+ #
808
+ def *(m) # m is matrix or vector or number
809
+ case(m)
810
+ when Numeric
811
+ rows = @rows.collect {|row|
812
+ row.collect {|e| e * m }
813
+ }
814
+ return new_matrix rows, column_size
815
+ when Vector
816
+ m = self.class.column_vector(m)
817
+ r = self * m
818
+ return r.column(0)
819
+ when Matrix
820
+ Matrix.Raise ErrDimensionMismatch if column_size != m.row_size
821
+
822
+ rows = Array.new(row_size) {|i|
823
+ Array.new(m.column_size) {|j|
824
+ (0 ... column_size).inject(0) do |vij, k|
825
+ vij + self[i, k] * m[k, j]
826
+ end
827
+ }
828
+ }
829
+ return new_matrix rows, m.column_size
830
+ else
831
+ return apply_through_coercion(m, __method__)
832
+ end
833
+ end
834
+
835
+ #
836
+ # Matrix addition.
837
+ # Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]]
838
+ # => 6 0
839
+ # -4 12
840
+ #
841
+ def +(m)
842
+ case m
843
+ when Numeric
844
+ Matrix.Raise ErrOperationNotDefined, "+", self.class, m.class
845
+ when Vector
846
+ m = self.class.column_vector(m)
847
+ when Matrix
848
+ else
849
+ return apply_through_coercion(m, __method__)
850
+ end
851
+
852
+ Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
853
+
854
+ rows = Array.new(row_size) {|i|
855
+ Array.new(column_size) {|j|
856
+ self[i, j] + m[i, j]
857
+ }
858
+ }
859
+ new_matrix rows, column_size
860
+ end
861
+
862
+ #
863
+ # Matrix subtraction.
864
+ # Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]]
865
+ # => -8 2
866
+ # 8 1
867
+ #
868
+ def -(m)
869
+ case m
870
+ when Numeric
871
+ Matrix.Raise ErrOperationNotDefined, "-", self.class, m.class
872
+ when Vector
873
+ m = self.class.column_vector(m)
874
+ when Matrix
875
+ else
876
+ return apply_through_coercion(m, __method__)
877
+ end
878
+
879
+ Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
880
+
881
+ rows = Array.new(row_size) {|i|
882
+ Array.new(column_size) {|j|
883
+ self[i, j] - m[i, j]
884
+ }
885
+ }
886
+ new_matrix rows, column_size
887
+ end
888
+
889
+ #
890
+ # Matrix division (multiplication by the inverse).
891
+ # Matrix[[7,6], [3,9]] / Matrix[[2,9], [3,1]]
892
+ # => -7 1
893
+ # -3 -6
894
+ #
895
+ def /(other)
896
+ case other
897
+ when Numeric
898
+ rows = @rows.collect {|row|
899
+ row.collect {|e| e / other }
900
+ }
901
+ return new_matrix rows, column_size
902
+ when Matrix
903
+ return self * other.inverse
904
+ else
905
+ return apply_through_coercion(other, __method__)
906
+ end
907
+ end
908
+
909
+ #
910
+ # Returns the inverse of the matrix.
911
+ # Matrix[[-1, -1], [0, -1]].inverse
912
+ # => -1 1
913
+ # 0 -1
914
+ #
915
+ def inverse
916
+ Matrix.Raise ErrDimensionMismatch unless square?
917
+ self.class.I(row_size).send(:inverse_from, self)
918
+ end
919
+ alias inv inverse
920
+
921
+ def inverse_from(src) # :nodoc:
922
+ last = row_size - 1
923
+ a = src.to_a
924
+
925
+ 0.upto(last) do |k|
926
+ i = k
927
+ akk = a[k][k].abs
928
+ (k+1).upto(last) do |j|
929
+ v = a[j][k].abs
930
+ if v > akk
931
+ i = j
932
+ akk = v
933
+ end
934
+ end
935
+ Matrix.Raise ErrNotRegular if akk == 0
936
+ if i != k
937
+ a[i], a[k] = a[k], a[i]
938
+ @rows[i], @rows[k] = @rows[k], @rows[i]
939
+ end
940
+ akk = a[k][k]
941
+
942
+ 0.upto(last) do |ii|
943
+ next if ii == k
944
+ q = a[ii][k].quo(akk)
945
+ a[ii][k] = 0
946
+
947
+ (k + 1).upto(last) do |j|
948
+ a[ii][j] -= a[k][j] * q
949
+ end
950
+ 0.upto(last) do |j|
951
+ @rows[ii][j] -= @rows[k][j] * q
952
+ end
953
+ end
954
+
955
+ (k+1).upto(last) do |j|
956
+ a[k][j] = a[k][j].quo(akk)
957
+ end
958
+ 0.upto(last) do |j|
959
+ @rows[k][j] = @rows[k][j].quo(akk)
960
+ end
961
+ end
962
+ self
963
+ end
964
+ private :inverse_from
965
+
966
+ #
967
+ # Matrix exponentiation.
968
+ # Equivalent to multiplying the matrix by itself N times.
969
+ # Non integer exponents will be handled by diagonalizing the matrix.
970
+ #
971
+ # Matrix[[7,6], [3,9]] ** 2
972
+ # => 67 96
973
+ # 48 99
974
+ #
975
+ def ** (other)
976
+ case other
977
+ when Integer
978
+ x = self
979
+ if other <= 0
980
+ x = self.inverse
981
+ return self.class.identity(self.column_size) if other == 0
982
+ other = -other
983
+ end
984
+ z = nil
985
+ loop do
986
+ z = z ? z * x : x if other[0] == 1
987
+ return z if (other >>= 1).zero?
988
+ x *= x
989
+ end
990
+ when Numeric
991
+ v, d, v_inv = eigensystem
992
+ v * self.class.diagonal(*d.each(:diagonal).map{|e| e ** other}) * v_inv
993
+ else
994
+ Matrix.Raise ErrOperationNotDefined, "**", self.class, other.class
995
+ end
996
+ end
997
+
998
+ #--
999
+ # MATRIX FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1000
+ #++
1001
+
1002
+ #
1003
+ # Returns the determinant of the matrix.
1004
+ #
1005
+ # Beware that using Float values can yield erroneous results
1006
+ # because of their lack of precision.
1007
+ # Consider using exact types like Rational or BigDecimal instead.
1008
+ #
1009
+ # Matrix[[7,6], [3,9]].determinant
1010
+ # => 45
1011
+ #
1012
+ def determinant
1013
+ Matrix.Raise ErrDimensionMismatch unless square?
1014
+ m = @rows
1015
+ case row_size
1016
+ # Up to 4x4, give result using Laplacian expansion by minors.
1017
+ # This will typically be faster, as well as giving good results
1018
+ # in case of Floats
1019
+ when 0
1020
+ +1
1021
+ when 1
1022
+ + m[0][0]
1023
+ when 2
1024
+ + m[0][0] * m[1][1] - m[0][1] * m[1][0]
1025
+ when 3
1026
+ m0, m1, m2 = m
1027
+ + m0[0] * m1[1] * m2[2] - m0[0] * m1[2] * m2[1] \
1028
+ - m0[1] * m1[0] * m2[2] + m0[1] * m1[2] * m2[0] \
1029
+ + m0[2] * m1[0] * m2[1] - m0[2] * m1[1] * m2[0]
1030
+ when 4
1031
+ m0, m1, m2, m3 = m
1032
+ + m0[0] * m1[1] * m2[2] * m3[3] - m0[0] * m1[1] * m2[3] * m3[2] \
1033
+ - m0[0] * m1[2] * m2[1] * m3[3] + m0[0] * m1[2] * m2[3] * m3[1] \
1034
+ + m0[0] * m1[3] * m2[1] * m3[2] - m0[0] * m1[3] * m2[2] * m3[1] \
1035
+ - m0[1] * m1[0] * m2[2] * m3[3] + m0[1] * m1[0] * m2[3] * m3[2] \
1036
+ + m0[1] * m1[2] * m2[0] * m3[3] - m0[1] * m1[2] * m2[3] * m3[0] \
1037
+ - m0[1] * m1[3] * m2[0] * m3[2] + m0[1] * m1[3] * m2[2] * m3[0] \
1038
+ + m0[2] * m1[0] * m2[1] * m3[3] - m0[2] * m1[0] * m2[3] * m3[1] \
1039
+ - m0[2] * m1[1] * m2[0] * m3[3] + m0[2] * m1[1] * m2[3] * m3[0] \
1040
+ + m0[2] * m1[3] * m2[0] * m3[1] - m0[2] * m1[3] * m2[1] * m3[0] \
1041
+ - m0[3] * m1[0] * m2[1] * m3[2] + m0[3] * m1[0] * m2[2] * m3[1] \
1042
+ + m0[3] * m1[1] * m2[0] * m3[2] - m0[3] * m1[1] * m2[2] * m3[0] \
1043
+ - m0[3] * m1[2] * m2[0] * m3[1] + m0[3] * m1[2] * m2[1] * m3[0]
1044
+ else
1045
+ # For bigger matrices, use an efficient and general algorithm.
1046
+ # Currently, we use the Gauss-Bareiss algorithm
1047
+ determinant_bareiss
1048
+ end
1049
+ end
1050
+ alias_method :det, :determinant
1051
+
1052
+ #
1053
+ # Private. Use Matrix#determinant
1054
+ #
1055
+ # Returns the determinant of the matrix, using
1056
+ # Bareiss' multistep integer-preserving gaussian elimination.
1057
+ # It has the same computational cost order O(n^3) as standard Gaussian elimination.
1058
+ # Intermediate results are fraction free and of lower complexity.
1059
+ # A matrix of Integers will have thus intermediate results that are also Integers,
1060
+ # with smaller bignums (if any), while a matrix of Float will usually have
1061
+ # intermediate results with better precision.
1062
+ #
1063
+ def determinant_bareiss
1064
+ size = row_size
1065
+ last = size - 1
1066
+ a = to_a
1067
+ no_pivot = Proc.new{ return 0 }
1068
+ sign = +1
1069
+ pivot = 1
1070
+ size.times do |k|
1071
+ previous_pivot = pivot
1072
+ if (pivot = a[k][k]) == 0
1073
+ switch = (k+1 ... size).find(no_pivot) {|row|
1074
+ a[row][k] != 0
1075
+ }
1076
+ a[switch], a[k] = a[k], a[switch]
1077
+ pivot = a[k][k]
1078
+ sign = -sign
1079
+ end
1080
+ (k+1).upto(last) do |i|
1081
+ ai = a[i]
1082
+ (k+1).upto(last) do |j|
1083
+ ai[j] = (pivot * ai[j] - ai[k] * a[k][j]) / previous_pivot
1084
+ end
1085
+ end
1086
+ end
1087
+ sign * pivot
1088
+ end
1089
+ private :determinant_bareiss
1090
+
1091
+ #
1092
+ # deprecated; use Matrix#determinant
1093
+ #
1094
+ def determinant_e
1095
+ warn "#{caller(1)[0]}: warning: Matrix#determinant_e is deprecated; use #determinant"
1096
+ rank
1097
+ end
1098
+ alias det_e determinant_e
1099
+
1100
+ #
1101
+ # Returns the rank of the matrix.
1102
+ # Beware that using Float values can yield erroneous results
1103
+ # because of their lack of precision.
1104
+ # Consider using exact types like Rational or BigDecimal instead.
1105
+ #
1106
+ # Matrix[[7,6], [3,9]].rank
1107
+ # => 2
1108
+ #
1109
+ def rank
1110
+ # We currently use Bareiss' multistep integer-preserving gaussian elimination
1111
+ # (see comments on determinant)
1112
+ a = to_a
1113
+ last_column = column_size - 1
1114
+ last_row = row_size - 1
1115
+ pivot_row = 0
1116
+ previous_pivot = 1
1117
+ 0.upto(last_column) do |k|
1118
+ switch_row = (pivot_row .. last_row).find {|row|
1119
+ a[row][k] != 0
1120
+ }
1121
+ if switch_row
1122
+ a[switch_row], a[pivot_row] = a[pivot_row], a[switch_row] unless pivot_row == switch_row
1123
+ pivot = a[pivot_row][k]
1124
+ (pivot_row+1).upto(last_row) do |i|
1125
+ ai = a[i]
1126
+ (k+1).upto(last_column) do |j|
1127
+ ai[j] = (pivot * ai[j] - ai[k] * a[pivot_row][j]) / previous_pivot
1128
+ end
1129
+ end
1130
+ pivot_row += 1
1131
+ previous_pivot = pivot
1132
+ end
1133
+ end
1134
+ pivot_row
1135
+ end
1136
+
1137
+ #
1138
+ # deprecated; use Matrix#rank
1139
+ #
1140
+ def rank_e
1141
+ warn "#{caller(1)[0]}: warning: Matrix#rank_e is deprecated; use #rank"
1142
+ rank
1143
+ end
1144
+
1145
+ # Returns a matrix with entries rounded to the given precision
1146
+ # (see Float#round)
1147
+ #
1148
+ def round(ndigits=0)
1149
+ map{|e| e.round(ndigits)}
1150
+ end
1151
+
1152
+ #
1153
+ # Returns the trace (sum of diagonal elements) of the matrix.
1154
+ # Matrix[[7,6], [3,9]].trace
1155
+ # => 16
1156
+ #
1157
+ def trace
1158
+ Matrix.Raise ErrDimensionMismatch unless square?
1159
+ (0...column_size).inject(0) do |tr, i|
1160
+ tr + @rows[i][i]
1161
+ end
1162
+ end
1163
+ alias tr trace
1164
+
1165
+ #
1166
+ # Returns the transpose of the matrix.
1167
+ # Matrix[[1,2], [3,4], [5,6]]
1168
+ # => 1 2
1169
+ # 3 4
1170
+ # 5 6
1171
+ # Matrix[[1,2], [3,4], [5,6]].transpose
1172
+ # => 1 3 5
1173
+ # 2 4 6
1174
+ #
1175
+ def transpose
1176
+ return self.class.empty(column_size, 0) if row_size.zero?
1177
+ new_matrix @rows.transpose, row_size
1178
+ end
1179
+ alias t transpose
1180
+
1181
+ #--
1182
+ # DECOMPOSITIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1183
+ #++
1184
+
1185
+ #
1186
+ # Returns the Eigensystem of the matrix; see +EigenvalueDecomposition+.
1187
+ # m = Matrix[[1, 2], [3, 4]]
1188
+ # v, d, v_inv = m.eigensystem
1189
+ # d.diagonal? # => true
1190
+ # v.inv == v_inv # => true
1191
+ # (v * d * v_inv).round(5) == m # => true
1192
+ #
1193
+ def eigensystem
1194
+ EigenvalueDecomposition.new(self)
1195
+ end
1196
+ alias eigen eigensystem
1197
+
1198
+ #
1199
+ # Returns the LUP decomposition of the matrix; see +LUPDecomposition+.
1200
+ # a = Matrix[[1, 2], [3, 4]]
1201
+ # l, u, p = a.lup
1202
+ # l.lower_triangular? # => true
1203
+ # u.upper_triangular? # => true
1204
+ # p.permutation? # => true
1205
+ # l * u == a * p # => true
1206
+ # a.lup.solve([2, 5]) # => Vector[(1/1), (1/2)]
1207
+ #
1208
+ def lup
1209
+ LUPDecomposition.new(self)
1210
+ end
1211
+ alias lup_decomposition lup
1212
+
1213
+ if 42.respond_to?(:conj)
1214
+ #--
1215
+ # COMPLEX ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1216
+ #++
1217
+
1218
+ #
1219
+ # Returns the conjugate of the matrix.
1220
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
1221
+ # => 1+2i i 0
1222
+ # 1 2 3
1223
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].conjugate
1224
+ # => 1-2i -i 0
1225
+ # 1 2 3
1226
+ #
1227
+ def conjugate
1228
+ collect(&:conjugate)
1229
+ end
1230
+ alias conj conjugate
1231
+
1232
+ #
1233
+ # Returns the imaginary part of the matrix.
1234
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
1235
+ # => 1+2i i 0
1236
+ # 1 2 3
1237
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].imaginary
1238
+ # => 2i i 0
1239
+ # 0 0 0
1240
+ #
1241
+ def imaginary
1242
+ collect(&:imaginary)
1243
+ end
1244
+ alias imag imaginary
1245
+
1246
+ #
1247
+ # Returns the real part of the matrix.
1248
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
1249
+ # => 1+2i i 0
1250
+ # 1 2 3
1251
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].real
1252
+ # => 1 0 0
1253
+ # 1 2 3
1254
+ #
1255
+ def real
1256
+ collect(&:real)
1257
+ end
1258
+
1259
+ #
1260
+ # Returns an array containing matrices corresponding to the real and imaginary
1261
+ # parts of the matrix
1262
+ #
1263
+ # m.rect == [m.real, m.imag] # ==> true for all matrices m
1264
+ #
1265
+ def rect
1266
+ [real, imag]
1267
+ end
1268
+ alias rectangular rect
1269
+ end
1270
+ #--
1271
+ # CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1272
+ #++
1273
+
1274
+ #
1275
+ # The coerce method provides support for Ruby type coercion.
1276
+ # This coercion mechanism is used by Ruby to handle mixed-type
1277
+ # numeric operations: it is intended to find a compatible common
1278
+ # type between the two operands of the operator.
1279
+ # See also Numeric#coerce.
1280
+ #
1281
+ def coerce(other)
1282
+ case other
1283
+ when Numeric
1284
+ return Scalar.new(other), self
1285
+ else
1286
+ raise TypeError, "#{self.class} can't be coerced into #{other.class}"
1287
+ end
1288
+ end
1289
+
1290
+ #
1291
+ # Returns an array of the row vectors of the matrix. See Vector.
1292
+ #
1293
+ def row_vectors
1294
+ Array.new(row_size) {|i|
1295
+ row(i)
1296
+ }
1297
+ end
1298
+
1299
+ #
1300
+ # Returns an array of the column vectors of the matrix. See Vector.
1301
+ #
1302
+ def column_vectors
1303
+ Array.new(column_size) {|i|
1304
+ column(i)
1305
+ }
1306
+ end
1307
+
1308
+ #
1309
+ # Returns an array of arrays that describe the rows of the matrix.
1310
+ #
1311
+ def to_a
1312
+ @rows.collect(&:dup)
1313
+ end
1314
+
1315
+ def elements_to_f
1316
+ warn "#{caller(1)[0]}: warning: Matrix#elements_to_f is deprecated, use map(&:to_f)"
1317
+ map(&:to_f)
1318
+ end
1319
+
1320
+ def elements_to_i
1321
+ warn "#{caller(1)[0]}: warning: Matrix#elements_to_i is deprecated, use map(&:to_i)"
1322
+ map(&:to_i)
1323
+ end
1324
+
1325
+ def elements_to_r
1326
+ warn "#{caller(1)[0]}: warning: Matrix#elements_to_r is deprecated, use map(&:to_r)"
1327
+ map(&:to_r)
1328
+ end
1329
+
1330
+ #--
1331
+ # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1332
+ #++
1333
+
1334
+ #
1335
+ # Overrides Object#to_s
1336
+ #
1337
+ def to_s
1338
+ if empty?
1339
+ "#{self.class}.empty(#{row_size}, #{column_size})"
1340
+ else
1341
+ "#{self.class}[" + @rows.collect{|row|
1342
+ "[" + row.collect{|e| e.to_s}.join(", ") + "]"
1343
+ }.join(", ")+"]"
1344
+ end
1345
+ end
1346
+
1347
+ #
1348
+ # Overrides Object#inspect
1349
+ #
1350
+ def inspect
1351
+ if empty?
1352
+ "#{self.class}.empty(#{row_size}, #{column_size})"
1353
+ else
1354
+ "#{self.class}#{@rows.inspect}"
1355
+ end
1356
+ end
1357
+
1358
+ # Private helper modules
1359
+
1360
+ module ConversionHelper # :nodoc:
1361
+ #
1362
+ # Converts the obj to an Array. If copy is set to true
1363
+ # a copy of obj will be made if necessary.
1364
+ #
1365
+ def convert_to_array(obj, copy = false) # :nodoc:
1366
+ case obj
1367
+ when Array
1368
+ copy ? obj.dup : obj
1369
+ when Vector
1370
+ obj.to_a
1371
+ else
1372
+ begin
1373
+ converted = obj.to_ary
1374
+ rescue Exception => e
1375
+ raise TypeError, "can't convert #{obj.class} into an Array (#{e.message})"
1376
+ end
1377
+ raise TypeError, "#{obj.class}#to_ary should return an Array" unless converted.is_a? Array
1378
+ converted
1379
+ end
1380
+ end
1381
+ private :convert_to_array
1382
+ end
1383
+
1384
+ extend ConversionHelper
1385
+
1386
+ module CoercionHelper # :nodoc:
1387
+ #
1388
+ # Applies the operator +oper+ with argument +obj+
1389
+ # through coercion of +obj+
1390
+ #
1391
+ def apply_through_coercion(obj, oper)
1392
+ coercion = obj.coerce(self)
1393
+ raise TypeError unless coercion.is_a?(Array) && coercion.length == 2
1394
+ coercion[0].public_send(oper, coercion[1])
1395
+ rescue
1396
+ raise TypeError, "#{obj.inspect} can't be coerced into #{self.class}"
1397
+ end
1398
+ private :apply_through_coercion
1399
+
1400
+ #
1401
+ # Helper method to coerce a value into a specific class.
1402
+ # Raises a TypeError if the coercion fails or the returned value
1403
+ # is not of the right class.
1404
+ # (from Rubinius)
1405
+ #
1406
+ def self.coerce_to(obj, cls, meth) # :nodoc:
1407
+ return obj if obj.kind_of?(cls)
1408
+
1409
+ begin
1410
+ ret = obj.__send__(meth)
1411
+ rescue Exception => e
1412
+ raise TypeError, "Coercion error: #{obj.inspect}.#{meth} => #{cls} failed:\n" \
1413
+ "(#{e.message})"
1414
+ end
1415
+ raise TypeError, "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{ret.class})" unless ret.kind_of? cls
1416
+ ret
1417
+ end
1418
+
1419
+ def self.coerce_to_int(obj)
1420
+ coerce_to(obj, Integer, :to_int)
1421
+ end
1422
+ end
1423
+
1424
+ include CoercionHelper
1425
+
1426
+ # Private CLASS
1427
+
1428
+ class Scalar < Numeric # :nodoc:
1429
+ include ExceptionForMatrix
1430
+ include CoercionHelper
1431
+
1432
+ def initialize(value)
1433
+ @value = value
1434
+ end
1435
+
1436
+ # ARITHMETIC
1437
+ def +(other)
1438
+ case other
1439
+ when Numeric
1440
+ Scalar.new(@value + other)
1441
+ when Vector, Matrix
1442
+ Scalar.Raise ErrOperationNotDefined, "+", @value.class, other.class
1443
+ else
1444
+ apply_through_coercion(other, __method__)
1445
+ end
1446
+ end
1447
+
1448
+ def -(other)
1449
+ case other
1450
+ when Numeric
1451
+ Scalar.new(@value - other)
1452
+ when Vector, Matrix
1453
+ Scalar.Raise ErrOperationNotDefined, "-", @value.class, other.class
1454
+ else
1455
+ apply_through_coercion(other, __method__)
1456
+ end
1457
+ end
1458
+
1459
+ def *(other)
1460
+ case other
1461
+ when Numeric
1462
+ Scalar.new(@value * other)
1463
+ when Vector, Matrix
1464
+ other.collect{|e| @value * e}
1465
+ else
1466
+ apply_through_coercion(other, __method__)
1467
+ end
1468
+ end
1469
+
1470
+ def / (other)
1471
+ case other
1472
+ when Numeric
1473
+ Scalar.new(@value / other)
1474
+ when Vector
1475
+ Scalar.Raise ErrOperationNotDefined, "/", @value.class, other.class
1476
+ when Matrix
1477
+ self * other.inverse
1478
+ else
1479
+ apply_through_coercion(other, __method__)
1480
+ end
1481
+ end
1482
+
1483
+ def ** (other)
1484
+ case other
1485
+ when Numeric
1486
+ Scalar.new(@value ** other)
1487
+ when Vector
1488
+ Scalar.Raise ErrOperationNotDefined, "**", @value.class, other.class
1489
+ when Matrix
1490
+ #other.powered_by(self)
1491
+ Scalar.Raise ErrOperationNotImplemented, "**", @value.class, other.class
1492
+ else
1493
+ apply_through_coercion(other, __method__)
1494
+ end
1495
+ end
1496
+ end
1497
+
1498
+ end
1499
+
1500
+
1501
+ #
1502
+ # The +Vector+ class represents a mathematical vector, which is useful in its own right, and
1503
+ # also constitutes a row or column of a Matrix.
1504
+ #
1505
+ # == Method Catalogue
1506
+ #
1507
+ # To create a Vector:
1508
+ # * Vector.[](*array)
1509
+ # * Vector.elements(array, copy = true)
1510
+ #
1511
+ # To access elements:
1512
+ # * #[](i)
1513
+ #
1514
+ # To enumerate the elements:
1515
+ # * #each2(v)
1516
+ # * #collect2(v)
1517
+ #
1518
+ # Vector arithmetic:
1519
+ # * #*(x) "is matrix or number"
1520
+ # * #+(v)
1521
+ # * #-(v)
1522
+ #
1523
+ # Vector functions:
1524
+ # * #inner_product(v)
1525
+ # * #collect
1526
+ # * #magnitude
1527
+ # * #map
1528
+ # * #map2(v)
1529
+ # * #norm
1530
+ # * #normalize
1531
+ # * #r
1532
+ # * #size
1533
+ #
1534
+ # Conversion to other data types:
1535
+ # * #covector
1536
+ # * #to_a
1537
+ # * #coerce(other)
1538
+ #
1539
+ # String representations:
1540
+ # * #to_s
1541
+ # * #inspect
1542
+ #
1543
+ class Vector
1544
+ include ExceptionForMatrix
1545
+ include Enumerable
1546
+ include Matrix::CoercionHelper
1547
+ extend Matrix::ConversionHelper
1548
+ #INSTANCE CREATION
1549
+
1550
+ private_class_method :new
1551
+ attr_reader :elements
1552
+ protected :elements
1553
+
1554
+ #
1555
+ # Creates a Vector from a list of elements.
1556
+ # Vector[7, 4, ...]
1557
+ #
1558
+ def Vector.[](*array)
1559
+ new convert_to_array(array, false)
1560
+ end
1561
+
1562
+ #
1563
+ # Creates a vector from an Array. The optional second argument specifies
1564
+ # whether the array itself or a copy is used internally.
1565
+ #
1566
+ def Vector.elements(array, copy = true)
1567
+ new convert_to_array(array, copy)
1568
+ end
1569
+
1570
+ #
1571
+ # Vector.new is private; use Vector[] or Vector.elements to create.
1572
+ #
1573
+ def initialize(array)
1574
+ # No checking is done at this point.
1575
+ @elements = array
1576
+ end
1577
+
1578
+ # ACCESSING
1579
+
1580
+ #
1581
+ # Returns element number +i+ (starting at zero) of the vector.
1582
+ #
1583
+ def [](i)
1584
+ @elements[i]
1585
+ end
1586
+ alias element []
1587
+ alias component []
1588
+
1589
+ def []=(i, v)
1590
+ @elements[i]= v
1591
+ end
1592
+ alias set_element []=
1593
+ alias set_component []=
1594
+ private :[]=, :set_element, :set_component
1595
+
1596
+ #
1597
+ # Returns the number of elements in the vector.
1598
+ #
1599
+ def size
1600
+ @elements.size
1601
+ end
1602
+
1603
+ #--
1604
+ # ENUMERATIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1605
+ #++
1606
+
1607
+ #
1608
+ # Iterate over the elements of this vector
1609
+ #
1610
+ def each(&block)
1611
+ return to_enum(:each) unless block_given?
1612
+ @elements.each(&block)
1613
+ self
1614
+ end
1615
+
1616
+ #
1617
+ # Iterate over the elements of this vector and +v+ in conjunction.
1618
+ #
1619
+ def each2(v) # :yield: e1, e2
1620
+ raise TypeError, "Integer is not like Vector" if v.kind_of?(Integer)
1621
+ Vector.Raise ErrDimensionMismatch if size != v.size
1622
+ return to_enum(:each2, v) unless block_given?
1623
+ size.times do |i|
1624
+ yield @elements[i], v[i]
1625
+ end
1626
+ self
1627
+ end
1628
+
1629
+ #
1630
+ # Collects (as in Enumerable#collect) over the elements of this vector and +v+
1631
+ # in conjunction.
1632
+ #
1633
+ def collect2(v) # :yield: e1, e2
1634
+ raise TypeError, "Integer is not like Vector" if v.kind_of?(Integer)
1635
+ Vector.Raise ErrDimensionMismatch if size != v.size
1636
+ return to_enum(:collect2, v) unless block_given?
1637
+ Array.new(size) do |i|
1638
+ yield @elements[i], v[i]
1639
+ end
1640
+ end
1641
+
1642
+ #--
1643
+ # COMPARING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1644
+ #++
1645
+
1646
+ #
1647
+ # Returns +true+ iff the two vectors have the same elements in the same order.
1648
+ #
1649
+ def ==(other)
1650
+ return false unless Vector === other
1651
+ @elements == other.elements
1652
+ end
1653
+
1654
+ def eql?(other)
1655
+ return false unless Vector === other
1656
+ @elements.eql? other.elements
1657
+ end
1658
+
1659
+ #
1660
+ # Return a copy of the vector.
1661
+ #
1662
+ def clone
1663
+ self.class.elements(@elements)
1664
+ end
1665
+
1666
+ #
1667
+ # Return a hash-code for the vector.
1668
+ #
1669
+ def hash
1670
+ @elements.hash
1671
+ end
1672
+
1673
+ #--
1674
+ # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1675
+ #++
1676
+
1677
+ #
1678
+ # Multiplies the vector by +x+, where +x+ is a number or another vector.
1679
+ #
1680
+ def *(x)
1681
+ case x
1682
+ when Numeric
1683
+ els = @elements.collect{|e| e * x}
1684
+ self.class.elements(els, false)
1685
+ when Matrix
1686
+ Matrix.column_vector(self) * x
1687
+ when Vector
1688
+ Vector.Raise ErrOperationNotDefined, "*", self.class, x.class
1689
+ else
1690
+ apply_through_coercion(x, __method__)
1691
+ end
1692
+ end
1693
+
1694
+ #
1695
+ # Vector addition.
1696
+ #
1697
+ def +(v)
1698
+ case v
1699
+ when Vector
1700
+ Vector.Raise ErrDimensionMismatch if size != v.size
1701
+ els = collect2(v) {|v1, v2|
1702
+ v1 + v2
1703
+ }
1704
+ self.class.elements(els, false)
1705
+ when Matrix
1706
+ Matrix.column_vector(self) + v
1707
+ else
1708
+ apply_through_coercion(v, __method__)
1709
+ end
1710
+ end
1711
+
1712
+ #
1713
+ # Vector subtraction.
1714
+ #
1715
+ def -(v)
1716
+ case v
1717
+ when Vector
1718
+ Vector.Raise ErrDimensionMismatch if size != v.size
1719
+ els = collect2(v) {|v1, v2|
1720
+ v1 - v2
1721
+ }
1722
+ self.class.elements(els, false)
1723
+ when Matrix
1724
+ Matrix.column_vector(self) - v
1725
+ else
1726
+ apply_through_coercion(v, __method__)
1727
+ end
1728
+ end
1729
+
1730
+ #
1731
+ # Vector division.
1732
+ #
1733
+ def /(x)
1734
+ case x
1735
+ when Numeric
1736
+ els = @elements.collect{|e| e / x}
1737
+ self.class.elements(els, false)
1738
+ when Matrix, Vector
1739
+ Vector.Raise ErrOperationNotDefined, "/", self.class, x.class
1740
+ else
1741
+ apply_through_coercion(x, __method__)
1742
+ end
1743
+ end
1744
+
1745
+ #--
1746
+ # VECTOR FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1747
+ #++
1748
+
1749
+ #
1750
+ # Returns the inner product of this vector with the other.
1751
+ # Vector[4,7].inner_product Vector[10,1] => 47
1752
+ #
1753
+ def inner_product(v)
1754
+ Vector.Raise ErrDimensionMismatch if size != v.size
1755
+
1756
+ p = 0
1757
+ each2(v) {|v1, v2|
1758
+ p += v1 * v2
1759
+ }
1760
+ p
1761
+ end
1762
+
1763
+ #
1764
+ # Like Array#collect.
1765
+ #
1766
+ def collect(&block) # :yield: e
1767
+ return to_enum(:collect) unless block_given?
1768
+ els = @elements.collect(&block)
1769
+ self.class.elements(els, false)
1770
+ end
1771
+ alias map collect
1772
+
1773
+ #
1774
+ # Returns the modulus (Pythagorean distance) of the vector.
1775
+ # Vector[5,8,2].r => 9.643650761
1776
+ #
1777
+ def magnitude
1778
+ Math.sqrt(@elements.inject(0) {|v, e| v + e*e})
1779
+ end
1780
+ alias r magnitude
1781
+ alias norm magnitude
1782
+
1783
+ #
1784
+ # Like Vector#collect2, but returns a Vector instead of an Array.
1785
+ #
1786
+ def map2(v, &block) # :yield: e1, e2
1787
+ return to_enum(:map2, v) unless block_given?
1788
+ els = collect2(v, &block)
1789
+ self.class.elements(els, false)
1790
+ end
1791
+
1792
+ class ZeroVectorError < StandardError
1793
+ end
1794
+ #
1795
+ # Returns a new vector with the same direction but with norm 1.
1796
+ # v = Vector[5,8,2].normalize
1797
+ # # => Vector[0.5184758473652127, 0.8295613557843402, 0.20739033894608505]
1798
+ # v.norm => 1.0
1799
+ #
1800
+ def normalize
1801
+ n = magnitude
1802
+ raise ZeroVectorError, "Zero vectors can not be normalized" if n == 0
1803
+ self / n
1804
+ end
1805
+
1806
+ #--
1807
+ # CONVERTING
1808
+ #++
1809
+
1810
+ #
1811
+ # Creates a single-row matrix from this vector.
1812
+ #
1813
+ def covector
1814
+ Matrix.row_vector(self)
1815
+ end
1816
+
1817
+ #
1818
+ # Returns the elements of the vector in an array.
1819
+ #
1820
+ def to_a
1821
+ @elements.dup
1822
+ end
1823
+
1824
+ def elements_to_f
1825
+ warn "#{caller(1)[0]}: warning: Vector#elements_to_f is deprecated"
1826
+ map(&:to_f)
1827
+ end
1828
+
1829
+ def elements_to_i
1830
+ warn "#{caller(1)[0]}: warning: Vector#elements_to_i is deprecated"
1831
+ map(&:to_i)
1832
+ end
1833
+
1834
+ def elements_to_r
1835
+ warn "#{caller(1)[0]}: warning: Vector#elements_to_r is deprecated"
1836
+ map(&:to_r)
1837
+ end
1838
+
1839
+ #
1840
+ # The coerce method provides support for Ruby type coercion.
1841
+ # This coercion mechanism is used by Ruby to handle mixed-type
1842
+ # numeric operations: it is intended to find a compatible common
1843
+ # type between the two operands of the operator.
1844
+ # See also Numeric#coerce.
1845
+ #
1846
+ def coerce(other)
1847
+ case other
1848
+ when Numeric
1849
+ return Matrix::Scalar.new(other), self
1850
+ else
1851
+ raise TypeError, "#{self.class} can't be coerced into #{other.class}"
1852
+ end
1853
+ end
1854
+
1855
+ #--
1856
+ # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1857
+ #++
1858
+
1859
+ #
1860
+ # Overrides Object#to_s
1861
+ #
1862
+ def to_s
1863
+ "Vector[" + @elements.join(", ") + "]"
1864
+ end
1865
+
1866
+ #
1867
+ # Overrides Object#inspect
1868
+ #
1869
+ def inspect
1870
+ "Vector" + @elements.inspect
1871
+ end
1872
+ end