rubysl-matrix 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +29 -0
  7. data/Rakefile +1 -0
  8. data/lib/matrix.rb +1 -0
  9. data/lib/rubysl/matrix.rb +2 -0
  10. data/lib/rubysl/matrix/matrix.rb +1537 -0
  11. data/lib/rubysl/matrix/version.rb +5 -0
  12. data/rubysl-matrix.gemspec +27 -0
  13. data/spec/I_spec.rb +5 -0
  14. data/spec/build_spec.rb +75 -0
  15. data/spec/clone_spec.rb +26 -0
  16. data/spec/coerce_spec.rb +11 -0
  17. data/spec/collect_spec.rb +5 -0
  18. data/spec/column_size_spec.rb +14 -0
  19. data/spec/column_spec.rb +36 -0
  20. data/spec/column_vector_spec.rb +36 -0
  21. data/spec/column_vectors_spec.rb +25 -0
  22. data/spec/columns_spec.rb +45 -0
  23. data/spec/conj_spec.rb +7 -0
  24. data/spec/conjugate_spec.rb +7 -0
  25. data/spec/constructor_spec.rb +68 -0
  26. data/spec/det_spec.rb +6 -0
  27. data/spec/determinant_spec.rb +6 -0
  28. data/spec/diagonal_spec.rb +73 -0
  29. data/spec/divide_spec.rb +60 -0
  30. data/spec/each_spec.rb +77 -0
  31. data/spec/each_with_index_spec.rb +84 -0
  32. data/spec/eigenvalue_decomposition/eigenvalue_matrix_spec.rb +10 -0
  33. data/spec/eigenvalue_decomposition/eigenvalues_spec.rb +23 -0
  34. data/spec/eigenvalue_decomposition/eigenvector_matrix_spec.rb +23 -0
  35. data/spec/eigenvalue_decomposition/eigenvectors_spec.rb +25 -0
  36. data/spec/eigenvalue_decomposition/initialize_spec.rb +27 -0
  37. data/spec/eigenvalue_decomposition/to_a_spec.rb +19 -0
  38. data/spec/element_reference_spec.rb +24 -0
  39. data/spec/empty_spec.rb +69 -0
  40. data/spec/eql_spec.rb +12 -0
  41. data/spec/equal_value_spec.rb +10 -0
  42. data/spec/exponent_spec.rb +66 -0
  43. data/spec/find_index_spec.rb +147 -0
  44. data/spec/fixtures/classes.rb +7 -0
  45. data/spec/hash_spec.rb +14 -0
  46. data/spec/hermitian_spec.rb +37 -0
  47. data/spec/identity_spec.rb +5 -0
  48. data/spec/imag_spec.rb +7 -0
  49. data/spec/imaginary_spec.rb +7 -0
  50. data/spec/inspect_spec.rb +30 -0
  51. data/spec/inv_spec.rb +6 -0
  52. data/spec/inverse_from_spec.rb +5 -0
  53. data/spec/inverse_spec.rb +6 -0
  54. data/spec/lower_triangular_spec.rb +25 -0
  55. data/spec/lup_decomposition/determinant_spec.rb +24 -0
  56. data/spec/lup_decomposition/initialize_spec.rb +14 -0
  57. data/spec/lup_decomposition/l_spec.rb +19 -0
  58. data/spec/lup_decomposition/p_spec.rb +19 -0
  59. data/spec/lup_decomposition/solve_spec.rb +54 -0
  60. data/spec/lup_decomposition/to_a_spec.rb +36 -0
  61. data/spec/lup_decomposition/u_spec.rb +19 -0
  62. data/spec/map_spec.rb +5 -0
  63. data/spec/minor_spec.rb +90 -0
  64. data/spec/minus_spec.rb +45 -0
  65. data/spec/multiply_spec.rb +73 -0
  66. data/spec/new_spec.rb +7 -0
  67. data/spec/normal_spec.rb +27 -0
  68. data/spec/orthogonal_spec.rb +27 -0
  69. data/spec/permutation_spec.rb +33 -0
  70. data/spec/plus_spec.rb +45 -0
  71. data/spec/rank_spec.rb +24 -0
  72. data/spec/real_spec.rb +45 -0
  73. data/spec/rect_spec.rb +7 -0
  74. data/spec/rectangular_spec.rb +7 -0
  75. data/spec/regular_spec.rb +34 -0
  76. data/spec/round_spec.rb +24 -0
  77. data/spec/row_size_spec.rb +12 -0
  78. data/spec/row_spec.rb +37 -0
  79. data/spec/row_vector_spec.rb +33 -0
  80. data/spec/row_vectors_spec.rb +25 -0
  81. data/spec/rows_spec.rb +40 -0
  82. data/spec/scalar/Fail_spec.rb +5 -0
  83. data/spec/scalar/Raise_spec.rb +5 -0
  84. data/spec/scalar/divide_spec.rb +5 -0
  85. data/spec/scalar/exponent_spec.rb +5 -0
  86. data/spec/scalar/included_spec.rb +5 -0
  87. data/spec/scalar/initialize_spec.rb +5 -0
  88. data/spec/scalar/minus_spec.rb +5 -0
  89. data/spec/scalar/multiply_spec.rb +5 -0
  90. data/spec/scalar/plus_spec.rb +5 -0
  91. data/spec/scalar_spec.rb +66 -0
  92. data/spec/shared/collect.rb +29 -0
  93. data/spec/shared/conjugate.rb +21 -0
  94. data/spec/shared/determinant.rb +53 -0
  95. data/spec/shared/equal_value.rb +35 -0
  96. data/spec/shared/identity.rb +21 -0
  97. data/spec/shared/imaginary.rb +22 -0
  98. data/spec/shared/inverse.rb +42 -0
  99. data/spec/shared/rectangular.rb +20 -0
  100. data/spec/shared/trace.rb +14 -0
  101. data/spec/shared/transpose.rb +23 -0
  102. data/spec/singular_spec.rb +34 -0
  103. data/spec/spec_helper.rb +35 -0
  104. data/spec/square_spec.rb +29 -0
  105. data/spec/symmetric_spec.rb +30 -0
  106. data/spec/t_spec.rb +5 -0
  107. data/spec/to_a_spec.rb +5 -0
  108. data/spec/to_s_spec.rb +5 -0
  109. data/spec/tr_spec.rb +6 -0
  110. data/spec/trace_spec.rb +6 -0
  111. data/spec/transpose_spec.rb +5 -0
  112. data/spec/unit_spec.rb +5 -0
  113. data/spec/unitary_spec.rb +29 -0
  114. data/spec/upper_triangular_spec.rb +26 -0
  115. data/spec/vector/cross_product_spec.rb +25 -0
  116. data/spec/vector/each2_spec.rb +52 -0
  117. data/spec/vector/eql_spec.rb +17 -0
  118. data/spec/vector/inner_product_spec.rb +25 -0
  119. data/spec/vector/normalize_spec.rb +19 -0
  120. data/spec/zero_spec.rb +55 -0
  121. metadata +341 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4352b6d39cda8317bc801f0ba18351a8bbfeb9c9
4
+ data.tar.gz: c99b10ce12cf8eefa06d89b89efe129d85ea63ce
5
+ SHA512:
6
+ metadata.gz: 9ff15f3449d2db32e3af7236d5a0b8b78bbfda777af44d1cc82d9a1c6bce6e6dbe7dc835bf9669f920a59519431f5e2b6e72efd12242f2258adccd35fe0b97c0
7
+ data.tar.gz: d0fe7fb661a965e5b9cf18357ebbb6b67af266cdb21f45445aca5bac276107f4427e6275679431c0b043d2906d25f85a52233cad6be8a7e196b2e779298fc2d7
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem update --system
4
+ - gem --version
5
+ - gem install rubysl-bundler
6
+ script: bundle exec mspec spec
7
+ rvm:
8
+ - rbx-nightly-18mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-matrix.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,29 @@
1
+ # Rubysl::Matrix
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-matrix'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-matrix
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ require "rubysl/matrix"
@@ -0,0 +1,2 @@
1
+ require "rubysl/matrix/matrix"
2
+ require "rubysl/matrix/version"
@@ -0,0 +1,1537 @@
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
+ # * <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>
48
+ #
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>
60
+ #
61
+ # Properties of a matrix:
62
+ # * <tt> #empty? </tt>
63
+ # * <tt> #real? </tt>
64
+ # * <tt> #regular? </tt>
65
+ # * <tt> #singular? </tt>
66
+ # * <tt> #square? </tt>
67
+ #
68
+ # 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>
76
+ #
77
+ # 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>
85
+ #
86
+ # 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>
94
+ #
95
+ # 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>
100
+ #
101
+ # String representations:
102
+ # * <tt> #to_s </tt>
103
+ # * <tt> #inspect </tt>
104
+ #
105
+ class Matrix
106
+ include Enumerable
107
+ include ExceptionForMatrix
108
+
109
+ # instance creations
110
+ private_class_method :new
111
+ attr_reader :rows
112
+ protected :rows
113
+
114
+ #
115
+ # Creates a matrix where each argument is a row.
116
+ # Matrix[ [25, 93], [-1, 66] ]
117
+ # => 25 93
118
+ # -1 66
119
+ #
120
+ def Matrix.[](*rows)
121
+ Matrix.rows(rows, false)
122
+ end
123
+
124
+ #
125
+ # Creates a matrix where +rows+ is an array of arrays, each of which is a row
126
+ # of the matrix. If the optional argument +copy+ is false, use the given
127
+ # arrays as the internal structure of the matrix without copying.
128
+ # Matrix.rows([[25, 93], [-1, 66]])
129
+ # => 25 93
130
+ # -1 66
131
+ #
132
+ def Matrix.rows(rows, copy = true)
133
+ rows = convert_to_array(rows)
134
+ rows.map! do |row|
135
+ convert_to_array(row, copy)
136
+ end
137
+ size = (rows[0] || []).size
138
+ rows.each do |row|
139
+ Matrix.Raise ErrDimensionMismatch, "row size differs (#{row.size} should be #{size})" unless row.size == size
140
+ end
141
+ new rows, size
142
+ end
143
+
144
+ #
145
+ # Creates a matrix using +columns+ as an array of column vectors.
146
+ # Matrix.columns([[25, 93], [-1, 66]])
147
+ # => 25 -1
148
+ # 93 66
149
+ #
150
+ def Matrix.columns(columns)
151
+ Matrix.rows(columns, false).transpose
152
+ end
153
+
154
+ #
155
+ # Creates a matrix of size +row_size+ x +column_size+.
156
+ # It fills the values by calling the given block,
157
+ # passing the current row and column.
158
+ # Returns an enumerator if no block is given.
159
+ #
160
+ # m = Matrix.build(2, 4) {|row, col| col - row }
161
+ # => Matrix[[0, 1, 2, 3], [-1, 0, 1, 2]]
162
+ # m = Matrix.build(3) { rand }
163
+ # => a 3x3 matrix with random elements
164
+ #
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|
172
+ yield i, j
173
+ end
174
+ end
175
+ new rows, column_size
176
+ end
177
+
178
+ #
179
+ # Creates a matrix where the diagonal elements are composed of +values+.
180
+ # Matrix.diagonal(9, 5, -3)
181
+ # => 9 0 0
182
+ # 0 5 0
183
+ # 0 0 -3
184
+ #
185
+ def Matrix.diagonal(*values)
186
+ size = values.size
187
+ rows = (0 ... size).collect {|j|
188
+ row = Array.new(size, 0)
189
+ row[j] = values[j]
190
+ row
191
+ }
192
+ new rows
193
+ end
194
+
195
+ #
196
+ # Creates an +n+ by +n+ diagonal matrix where each diagonal element is
197
+ # +value+.
198
+ # Matrix.scalar(2, 5)
199
+ # => 5 0
200
+ # 0 5
201
+ #
202
+ def Matrix.scalar(n, value)
203
+ Matrix.diagonal(*Array.new(n, value))
204
+ end
205
+
206
+ #
207
+ # Creates an +n+ by +n+ identity matrix.
208
+ # Matrix.identity(2)
209
+ # => 1 0
210
+ # 0 1
211
+ #
212
+ def Matrix.identity(n)
213
+ Matrix.scalar(n, 1)
214
+ end
215
+ class << Matrix
216
+ alias unit identity
217
+ alias I identity
218
+ end
219
+
220
+ #
221
+ # Creates an +n+ by +n+ zero matrix.
222
+ # Matrix.zero(2)
223
+ # => 0 0
224
+ # 0 0
225
+ #
226
+ def Matrix.zero(n)
227
+ Matrix.scalar(n, 0)
228
+ end
229
+
230
+ #
231
+ # Creates a single-row matrix where the values of that row are as given in
232
+ # +row+.
233
+ # Matrix.row_vector([4,5,6])
234
+ # => 4 5 6
235
+ #
236
+ def Matrix.row_vector(row)
237
+ row = convert_to_array(row)
238
+ new [row]
239
+ end
240
+
241
+ #
242
+ # Creates a single-column matrix where the values of that column are as given
243
+ # in +column+.
244
+ # Matrix.column_vector([4,5,6])
245
+ # => 4
246
+ # 5
247
+ # 6
248
+ #
249
+ def Matrix.column_vector(column)
250
+ column = convert_to_array(column)
251
+ new [column].transpose, 1
252
+ end
253
+
254
+ #
255
+ # Creates a empty matrix of +row_size+ x +column_size+.
256
+ # At least one of +row_size+ or +column_size+ must be 0.
257
+ #
258
+ # m = Matrix.empty(2, 0)
259
+ # m == Matrix[ [], [] ]
260
+ # => true
261
+ # n = Matrix.empty(0, 3)
262
+ # n == Matrix.columns([ [], [], [] ])
263
+ # => true
264
+ # m * n
265
+ # => Matrix[[0, 0, 0], [0, 0, 0]]
266
+ #
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
270
+
271
+ new([[]]*row_size, column_size)
272
+ end
273
+
274
+ #
275
+ # Matrix.new is private; use Matrix.rows, columns, [], etc... to create.
276
+ #
277
+ def initialize(rows, column_size = rows[0].size)
278
+ # 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,
280
+ # otherwise it *must* be specified and can be any integer >= 0
281
+ @rows = rows
282
+ @column_size = column_size
283
+ end
284
+
285
+ def new_matrix(rows, column_size = rows[0].size) # :nodoc:
286
+ Matrix.send(:new, rows, column_size) # bypass privacy of Matrix.new
287
+ end
288
+ private :new_matrix
289
+
290
+ #
291
+ # Returns element (+i+,+j+) of the matrix. That is: row +i+, column +j+.
292
+ #
293
+ def [](i, j)
294
+ @rows.fetch(i){return nil}[j]
295
+ end
296
+ alias element []
297
+ alias component []
298
+
299
+ def []=(i, j, v)
300
+ @rows[i][j] = v
301
+ end
302
+ alias set_element []=
303
+ alias set_component []=
304
+ private :[]=, :set_element, :set_component
305
+
306
+ #
307
+ # Returns the number of rows.
308
+ #
309
+ def row_size
310
+ @rows.size
311
+ end
312
+
313
+ #
314
+ # Returns the number of columns.
315
+ #
316
+ attr_reader :column_size
317
+
318
+ #
319
+ # Returns row vector number +i+ of the matrix as a Vector (starting at 0 like
320
+ # an array). When a block is given, the elements of that vector are iterated.
321
+ #
322
+ def row(i, &block) # :yield: e
323
+ if block_given?
324
+ @rows.fetch(i){return self}.each(&block)
325
+ self
326
+ else
327
+ Vector.elements(@rows.fetch(i){return nil})
328
+ end
329
+ end
330
+
331
+ #
332
+ # Returns column vector number +j+ of the matrix as a Vector (starting at 0
333
+ # like an array). When a block is given, the elements of that vector are
334
+ # iterated.
335
+ #
336
+ def column(j) # :yield: e
337
+ if block_given?
338
+ return self if j >= column_size || j < -column_size
339
+ row_size.times do |i|
340
+ yield @rows[i][j]
341
+ end
342
+ self
343
+ else
344
+ return nil if j >= column_size || j < -column_size
345
+ col = (0 ... row_size).collect {|i|
346
+ @rows[i][j]
347
+ }
348
+ Vector.elements(col, false)
349
+ end
350
+ end
351
+
352
+ #
353
+ # Returns a matrix that is the result of iteration of the given block over all
354
+ # elements of the matrix.
355
+ # Matrix[ [1,2], [3,4] ].collect { |e| e**2 }
356
+ # => 1 4
357
+ # 9 16
358
+ #
359
+ def collect(&block) # :yield: e
360
+ return to_enum(:collect) unless block_given?
361
+ rows = @rows.collect{|row| row.collect(&block)}
362
+ new_matrix rows, column_size
363
+ end
364
+ alias map collect
365
+
366
+ #
367
+ # Yields all elements of the matrix, starting with those of the first row,
368
+ # or returns an Enumerator is no block given
369
+ # Matrix[ [1,2], [3,4] ].each { |e| puts e }
370
+ # # => 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)
376
+ end
377
+ self
378
+ end
379
+
380
+ #
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
384
+ # Matrix[ [1,2], [3,4] ].each_with_index do |e, row, col|
385
+ # puts "#{e} at #{row}, #{col}"
386
+ # 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
397
+ end
398
+ end
399
+ self
400
+ end
401
+
402
+ #
403
+ # Returns a section of the matrix. The parameters are either:
404
+ # * start_row, nrows, start_col, ncols; OR
405
+ # * row_range, col_range
406
+ #
407
+ # Matrix.diagonal(9, 5, -3).minor(0..1, 0..2)
408
+ # => 9 0 0
409
+ # 0 5 0
410
+ #
411
+ # Like Array#[], negative indices count backward from the end of the
412
+ # 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.
414
+ #
415
+ def minor(*param)
416
+ case param.size
417
+ when 2
418
+ row_range, col_range = param
419
+ from_row = row_range.first
420
+ from_row += row_size if from_row < 0
421
+ to_row = row_range.end
422
+ to_row += row_size if to_row < 0
423
+ to_row += 1 unless row_range.exclude_end?
424
+ size_row = to_row - from_row
425
+
426
+ from_col = col_range.first
427
+ from_col += column_size if from_col < 0
428
+ to_col = col_range.end
429
+ to_col += column_size if to_col < 0
430
+ to_col += 1 unless col_range.exclude_end?
431
+ size_col = to_col - from_col
432
+ when 4
433
+ from_row, size_row, from_col, size_col = param
434
+ 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
437
+ else
438
+ Matrix.Raise ArgumentError, param.inspect
439
+ end
440
+
441
+ return nil if from_row > row_size || from_col > column_size || from_row < 0 || from_col < 0
442
+ rows = @rows[from_row, size_row].collect{|row|
443
+ row[from_col, size_col]
444
+ }
445
+ new_matrix rows, column_size - from_col
446
+ end
447
+
448
+ #--
449
+ # TESTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
450
+ #++
451
+
452
+ #
453
+ # Returns +true+ if this is an empty matrix, i.e. if the number of rows
454
+ # or the number of columns is 0.
455
+ #
456
+ def empty?
457
+ column_size == 0 || row_size == 0
458
+ end
459
+
460
+ #
461
+ # Returns +true+ if all entries of the matrix are real.
462
+ #
463
+ def real?
464
+ all?(&:real?)
465
+ end
466
+
467
+ #
468
+ # Returns +true+ if this is a regular (i.e. non-singular) matrix.
469
+ #
470
+ def regular?
471
+ not singular?
472
+ end
473
+
474
+ #
475
+ # Returns +true+ is this is a singular matrix.
476
+ #
477
+ def singular?
478
+ determinant == 0
479
+ end
480
+
481
+ #
482
+ # Returns +true+ is this is a square matrix.
483
+ #
484
+ def square?
485
+ column_size == row_size
486
+ end
487
+
488
+ #--
489
+ # OBJECT METHODS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
490
+ #++
491
+
492
+ #
493
+ # Returns +true+ if and only if the two matrices contain equal elements.
494
+ #
495
+ def ==(other)
496
+ return false unless Matrix === other
497
+ rows == other.rows
498
+ end
499
+
500
+ def eql?(other)
501
+ return false unless Matrix === other
502
+ rows.eql? other.rows
503
+ end
504
+
505
+ #
506
+ # Returns a clone of the matrix, so that the contents of each do not reference
507
+ # identical objects.
508
+ # There should be no good reason to do this since Matrices are immutable.
509
+ #
510
+ def clone
511
+ new_matrix @rows.map(&:dup), column_size
512
+ end
513
+
514
+ #
515
+ # Returns a hash-code for the matrix.
516
+ #
517
+ def hash
518
+ @rows.hash
519
+ end
520
+
521
+ #--
522
+ # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
523
+ #++
524
+
525
+ #
526
+ # Matrix multiplication.
527
+ # Matrix[[2,4], [6,8]] * Matrix.identity(2)
528
+ # => 2 4
529
+ # 6 8
530
+ #
531
+ def *(m) # m is matrix or vector or number
532
+ case(m)
533
+ when Numeric
534
+ rows = @rows.collect {|row|
535
+ row.collect {|e|
536
+ e * m
537
+ }
538
+ }
539
+ return new_matrix rows, column_size
540
+ when Vector
541
+ m = Matrix.column_vector(m)
542
+ r = self * m
543
+ return r.column(0)
544
+ when Matrix
545
+ Matrix.Raise ErrDimensionMismatch if column_size != m.row_size
546
+
547
+ rows = (0 ... row_size).collect {|i|
548
+ (0 ... m.column_size).collect {|j|
549
+ (0 ... column_size).inject(0) do |vij, k|
550
+ vij + self[i, k] * m[k, j]
551
+ end
552
+ }
553
+ }
554
+ return new_matrix rows, m.column_size
555
+ else
556
+ return apply_through_coercion(m, __method__)
557
+ end
558
+ end
559
+
560
+ #
561
+ # Matrix addition.
562
+ # Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]]
563
+ # => 6 0
564
+ # -4 12
565
+ #
566
+ def +(m)
567
+ case m
568
+ when Numeric
569
+ Matrix.Raise ErrOperationNotDefined, "+", self.class, m.class
570
+ when Vector
571
+ m = Matrix.column_vector(m)
572
+ when Matrix
573
+ else
574
+ return apply_through_coercion(m, __method__)
575
+ end
576
+
577
+ Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
578
+
579
+ rows = (0 ... row_size).collect {|i|
580
+ (0 ... column_size).collect {|j|
581
+ self[i, j] + m[i, j]
582
+ }
583
+ }
584
+ new_matrix rows, column_size
585
+ end
586
+
587
+ #
588
+ # Matrix subtraction.
589
+ # Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]]
590
+ # => -8 2
591
+ # 8 1
592
+ #
593
+ def -(m)
594
+ case m
595
+ when Numeric
596
+ Matrix.Raise ErrOperationNotDefined, "-", self.class, m.class
597
+ when Vector
598
+ m = Matrix.column_vector(m)
599
+ when Matrix
600
+ else
601
+ return apply_through_coercion(m, __method__)
602
+ end
603
+
604
+ Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
605
+
606
+ rows = (0 ... row_size).collect {|i|
607
+ (0 ... column_size).collect {|j|
608
+ self[i, j] - m[i, j]
609
+ }
610
+ }
611
+ new_matrix rows, column_size
612
+ end
613
+
614
+ #
615
+ # Matrix division (multiplication by the inverse).
616
+ # Matrix[[7,6], [3,9]] / Matrix[[2,9], [3,1]]
617
+ # => -7 1
618
+ # -3 -6
619
+ #
620
+ def /(other)
621
+ case other
622
+ when Numeric
623
+ rows = @rows.collect {|row|
624
+ row.collect {|e|
625
+ e / other
626
+ }
627
+ }
628
+ return new_matrix rows, column_size
629
+ when Matrix
630
+ return self * other.inverse
631
+ else
632
+ return apply_through_coercion(other, __method__)
633
+ end
634
+ end
635
+
636
+ #
637
+ # Returns the inverse of the matrix.
638
+ # Matrix[[-1, -1], [0, -1]].inverse
639
+ # => -1 1
640
+ # 0 -1
641
+ #
642
+ def inverse
643
+ Matrix.Raise ErrDimensionMismatch unless square?
644
+ Matrix.I(row_size).send(:inverse_from, self)
645
+ end
646
+ alias inv inverse
647
+
648
+ def inverse_from(src) # :nodoc:
649
+ last = row_size - 1
650
+ a = src.to_a
651
+
652
+ 0.upto(last) do |k|
653
+ i = k
654
+ akk = a[k][k].abs
655
+ (k+1).upto(last) do |j|
656
+ v = a[j][k].abs
657
+ if v > akk
658
+ i = j
659
+ akk = v
660
+ end
661
+ end
662
+ Matrix.Raise ErrNotRegular if akk == 0
663
+ if i != k
664
+ a[i], a[k] = a[k], a[i]
665
+ @rows[i], @rows[k] = @rows[k], @rows[i]
666
+ end
667
+ akk = a[k][k]
668
+
669
+ 0.upto(last) do |ii|
670
+ next if ii == k
671
+ q = a[ii][k].quo(akk)
672
+ a[ii][k] = 0
673
+
674
+ (k + 1).upto(last) do |j|
675
+ a[ii][j] -= a[k][j] * q
676
+ end
677
+ 0.upto(last) do |j|
678
+ @rows[ii][j] -= @rows[k][j] * q
679
+ end
680
+ end
681
+
682
+ (k+1).upto(last) do |j|
683
+ a[k][j] = a[k][j].quo(akk)
684
+ end
685
+ 0.upto(last) do |j|
686
+ @rows[k][j] = @rows[k][j].quo(akk)
687
+ end
688
+ end
689
+ self
690
+ end
691
+ private :inverse_from
692
+
693
+ #
694
+ # Matrix exponentiation. Currently implemented for integer powers only.
695
+ # Equivalent to multiplying the matrix by itself N times.
696
+ # Matrix[[7,6], [3,9]] ** 2
697
+ # => 67 96
698
+ # 48 99
699
+ #
700
+ def ** (other)
701
+ case other
702
+ when Integer
703
+ x = self
704
+ if other <= 0
705
+ x = self.inverse
706
+ return Matrix.identity(self.column_size) if other == 0
707
+ other = -other
708
+ end
709
+ z = nil
710
+ loop do
711
+ z = z ? z * x : x if other[0] == 1
712
+ return z if (other >>= 1).zero?
713
+ x *= x
714
+ end
715
+ else
716
+ Matrix.Raise ErrOperationNotDefined, "**", self.class, other.class
717
+ end
718
+ end
719
+
720
+ #--
721
+ # MATRIX FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
722
+ #++
723
+
724
+ #
725
+ # Returns the determinant of the matrix.
726
+ #
727
+ # Beware that using Float values can yield erroneous results
728
+ # because of their lack of precision.
729
+ # Consider using exact types like Rational or BigDecimal instead.
730
+ #
731
+ # Matrix[[7,6], [3,9]].determinant
732
+ # => 45
733
+ #
734
+ def determinant
735
+ Matrix.Raise ErrDimensionMismatch unless square?
736
+ m = @rows
737
+ case row_size
738
+ # Up to 4x4, give result using Laplacian expansion by minors.
739
+ # This will typically be faster, as well as giving good results
740
+ # in case of Floats
741
+ when 0
742
+ +1
743
+ when 1
744
+ + m[0][0]
745
+ when 2
746
+ + m[0][0] * m[1][1] - m[0][1] * m[1][0]
747
+ when 3
748
+ m0, m1, m2 = m
749
+ + m0[0] * m1[1] * m2[2] - m0[0] * m1[2] * m2[1] \
750
+ - m0[1] * m1[0] * m2[2] + m0[1] * m1[2] * m2[0] \
751
+ + m0[2] * m1[0] * m2[1] - m0[2] * m1[1] * m2[0]
752
+ when 4
753
+ m0, m1, m2, m3 = m
754
+ + m0[0] * m1[1] * m2[2] * m3[3] - m0[0] * m1[1] * m2[3] * m3[2] \
755
+ - m0[0] * m1[2] * m2[1] * m3[3] + m0[0] * m1[2] * m2[3] * m3[1] \
756
+ + m0[0] * m1[3] * m2[1] * m3[2] - m0[0] * m1[3] * m2[2] * m3[1] \
757
+ - m0[1] * m1[0] * m2[2] * m3[3] + m0[1] * m1[0] * m2[3] * m3[2] \
758
+ + m0[1] * m1[2] * m2[0] * m3[3] - m0[1] * m1[2] * m2[3] * m3[0] \
759
+ - m0[1] * m1[3] * m2[0] * m3[2] + m0[1] * m1[3] * m2[2] * m3[0] \
760
+ + m0[2] * m1[0] * m2[1] * m3[3] - m0[2] * m1[0] * m2[3] * m3[1] \
761
+ - m0[2] * m1[1] * m2[0] * m3[3] + m0[2] * m1[1] * m2[3] * m3[0] \
762
+ + m0[2] * m1[3] * m2[0] * m3[1] - m0[2] * m1[3] * m2[1] * m3[0] \
763
+ - m0[3] * m1[0] * m2[1] * m3[2] + m0[3] * m1[0] * m2[2] * m3[1] \
764
+ + m0[3] * m1[1] * m2[0] * m3[2] - m0[3] * m1[1] * m2[2] * m3[0] \
765
+ - m0[3] * m1[2] * m2[0] * m3[1] + m0[3] * m1[2] * m2[1] * m3[0]
766
+ else
767
+ # For bigger matrices, use an efficient and general algorithm.
768
+ # Currently, we use the Gauss-Bareiss algorithm
769
+ determinant_bareiss
770
+ end
771
+ end
772
+ alias_method :det, :determinant
773
+
774
+ #
775
+ # Private. Use Matrix#determinant
776
+ #
777
+ # Returns the determinant of the matrix, using
778
+ # Bareiss' multistep integer-preserving gaussian elimination.
779
+ # It has the same computational cost order O(n^3) as standard Gaussian elimination.
780
+ # Intermediate results are fraction free and of lower complexity.
781
+ # A matrix of Integers will have thus intermediate results that are also Integers,
782
+ # with smaller bignums (if any), while a matrix of Float will usually have
783
+ # intermediate results with better precision.
784
+ #
785
+ def determinant_bareiss
786
+ size = row_size
787
+ last = size - 1
788
+ a = to_a
789
+ no_pivot = Proc.new{ return 0 }
790
+ sign = +1
791
+ pivot = 1
792
+ size.times do |k|
793
+ previous_pivot = pivot
794
+ if (pivot = a[k][k]) == 0
795
+ switch = (k+1 ... size).find(no_pivot) {|row|
796
+ a[row][k] != 0
797
+ }
798
+ a[switch], a[k] = a[k], a[switch]
799
+ pivot = a[k][k]
800
+ sign = -sign
801
+ end
802
+ (k+1).upto(last) do |i|
803
+ ai = a[i]
804
+ (k+1).upto(last) do |j|
805
+ ai[j] = (pivot * ai[j] - ai[k] * a[k][j]) / previous_pivot
806
+ end
807
+ end
808
+ end
809
+ sign * pivot
810
+ end
811
+ private :determinant_bareiss
812
+
813
+ #
814
+ # deprecated; use Matrix#determinant
815
+ #
816
+ def determinant_e
817
+ warn "#{caller(1)[0]}: warning: Matrix#determinant_e is deprecated; use #determinant"
818
+ rank
819
+ end
820
+ alias det_e determinant_e
821
+
822
+ #
823
+ # Returns the rank of the matrix.
824
+ # Beware that using Float values can yield erroneous results
825
+ # because of their lack of precision.
826
+ # Consider using exact types like Rational or BigDecimal instead.
827
+ #
828
+ # Matrix[[7,6], [3,9]].rank
829
+ # => 2
830
+ #
831
+ def rank
832
+ # We currently use Bareiss' multistep integer-preserving gaussian elimination
833
+ # (see comments on determinant)
834
+ a = to_a
835
+ last_column = column_size - 1
836
+ last_row = row_size - 1
837
+ rank = 0
838
+ pivot_row = 0
839
+ previous_pivot = 1
840
+ 0.upto(last_column) do |k|
841
+ switch_row = (pivot_row .. last_row).find {|row|
842
+ a[row][k] != 0
843
+ }
844
+ if switch_row
845
+ a[switch_row], a[pivot_row] = a[pivot_row], a[switch_row] unless pivot_row == switch_row
846
+ pivot = a[pivot_row][k]
847
+ (pivot_row+1).upto(last_row) do |i|
848
+ ai = a[i]
849
+ (k+1).upto(last_column) do |j|
850
+ ai[j] = (pivot * ai[j] - ai[k] * a[pivot_row][j]) / previous_pivot
851
+ end
852
+ end
853
+ pivot_row += 1
854
+ previous_pivot = pivot
855
+ end
856
+ end
857
+ pivot_row
858
+ end
859
+
860
+ #
861
+ # deprecated; use Matrix#rank
862
+ #
863
+ def rank_e
864
+ warn "#{caller(1)[0]}: warning: Matrix#rank_e is deprecated; use #rank"
865
+ rank
866
+ end
867
+
868
+
869
+ #
870
+ # Returns the trace (sum of diagonal elements) of the matrix.
871
+ # Matrix[[7,6], [3,9]].trace
872
+ # => 16
873
+ #
874
+ def trace
875
+ Matrix.Raise ErrDimensionMismatch unless square?
876
+ (0...column_size).inject(0) do |tr, i|
877
+ tr + @rows[i][i]
878
+ end
879
+ end
880
+ alias tr trace
881
+
882
+ #
883
+ # Returns the transpose of the matrix.
884
+ # Matrix[[1,2], [3,4], [5,6]]
885
+ # => 1 2
886
+ # 3 4
887
+ # 5 6
888
+ # Matrix[[1,2], [3,4], [5,6]].transpose
889
+ # => 1 3 5
890
+ # 2 4 6
891
+ #
892
+ def transpose
893
+ return Matrix.empty(column_size, 0) if row_size.zero?
894
+ new_matrix @rows.transpose, row_size
895
+ end
896
+ alias t transpose
897
+
898
+ #--
899
+ # COMPLEX ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
900
+ #++
901
+
902
+ #
903
+ # Returns the conjugate of the matrix.
904
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
905
+ # => 1+2i i 0
906
+ # 1 2 3
907
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].conjugate
908
+ # => 1-2i -i 0
909
+ # 1 2 3
910
+ #
911
+ def conjugate
912
+ collect(&:conjugate)
913
+ end
914
+ alias conj conjugate
915
+
916
+ #
917
+ # Returns the imaginary part of the matrix.
918
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
919
+ # => 1+2i i 0
920
+ # 1 2 3
921
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].imaginary
922
+ # => 2i i 0
923
+ # 0 0 0
924
+ #
925
+ def imaginary
926
+ collect(&:imaginary)
927
+ end
928
+ alias imag imaginary
929
+
930
+ #
931
+ # Returns the real part of the matrix.
932
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
933
+ # => 1+2i i 0
934
+ # 1 2 3
935
+ # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].real
936
+ # => 1 0 0
937
+ # 1 2 3
938
+ #
939
+ def real
940
+ collect(&:real)
941
+ end
942
+
943
+ #
944
+ # Returns an array containing matrices corresponding to the real and imaginary
945
+ # parts of the matrix
946
+ #
947
+ # m.rect == [m.real, m.imag] # ==> true for all matrices m
948
+ #
949
+ def rect
950
+ [real, imag]
951
+ end
952
+ alias rectangular rect
953
+
954
+ #--
955
+ # CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
956
+ #++
957
+
958
+ #
959
+ # The coerce method provides support for Ruby type coercion.
960
+ # This coercion mechanism is used by Ruby to handle mixed-type
961
+ # numeric operations: it is intended to find a compatible common
962
+ # type between the two operands of the operator.
963
+ # See also Numeric#coerce.
964
+ #
965
+ def coerce(other)
966
+ case other
967
+ when Numeric
968
+ return Scalar.new(other), self
969
+ else
970
+ raise TypeError, "#{self.class} can't be coerced into #{other.class}"
971
+ end
972
+ end
973
+
974
+ #
975
+ # Returns an array of the row vectors of the matrix. See Vector.
976
+ #
977
+ def row_vectors
978
+ (0 ... row_size).collect {|i|
979
+ row(i)
980
+ }
981
+ end
982
+
983
+ #
984
+ # Returns an array of the column vectors of the matrix. See Vector.
985
+ #
986
+ def column_vectors
987
+ (0 ... column_size).collect {|i|
988
+ column(i)
989
+ }
990
+ end
991
+
992
+ #
993
+ # Returns an array of arrays that describe the rows of the matrix.
994
+ #
995
+ def to_a
996
+ @rows.collect{|row| row.dup}
997
+ end
998
+
999
+ def elements_to_f
1000
+ warn "#{caller(1)[0]}: warning: Matrix#elements_to_f is deprecated, use map(&:to_f)"
1001
+ map(&:to_f)
1002
+ end
1003
+
1004
+ def elements_to_i
1005
+ warn "#{caller(1)[0]}: warning: Matrix#elements_to_i is deprecated, use map(&:to_i)"
1006
+ map(&:to_i)
1007
+ end
1008
+
1009
+ def elements_to_r
1010
+ warn "#{caller(1)[0]}: warning: Matrix#elements_to_r is deprecated, use map(&:to_r)"
1011
+ map(&:to_r)
1012
+ end
1013
+
1014
+ #--
1015
+ # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1016
+ #++
1017
+
1018
+ #
1019
+ # Overrides Object#to_s
1020
+ #
1021
+ def to_s
1022
+ if empty?
1023
+ "Matrix.empty(#{row_size}, #{column_size})"
1024
+ else
1025
+ "Matrix[" + @rows.collect{|row|
1026
+ "[" + row.collect{|e| e.to_s}.join(", ") + "]"
1027
+ }.join(", ")+"]"
1028
+ end
1029
+ end
1030
+
1031
+ #
1032
+ # Overrides Object#inspect
1033
+ #
1034
+ def inspect
1035
+ if empty?
1036
+ "Matrix.empty(#{row_size}, #{column_size})"
1037
+ else
1038
+ "Matrix#{@rows.inspect}"
1039
+ end
1040
+ end
1041
+
1042
+ # Private helper modules
1043
+
1044
+ module ConversionHelper # :nodoc:
1045
+ #
1046
+ # Converts the obj to an Array. If copy is set to true
1047
+ # a copy of obj will be made if necessary.
1048
+ #
1049
+ def convert_to_array(obj, copy = false) # :nodoc:
1050
+ case obj
1051
+ when Array
1052
+ copy ? obj.dup : obj
1053
+ when Vector
1054
+ obj.to_a
1055
+ else
1056
+ begin
1057
+ converted = obj.to_ary
1058
+ rescue Exception => e
1059
+ raise TypeError, "can't convert #{obj.class} into an Array (#{e.message})"
1060
+ end
1061
+ raise TypeError, "#{obj.class}#to_ary should return an Array" unless converted.is_a? Array
1062
+ converted
1063
+ end
1064
+ end
1065
+ private :convert_to_array
1066
+ end
1067
+
1068
+ extend ConversionHelper
1069
+
1070
+ module CoercionHelper # :nodoc:
1071
+ #
1072
+ # Applies the operator +oper+ with argument +obj+
1073
+ # through coercion of +obj+
1074
+ #
1075
+ def apply_through_coercion(obj, oper)
1076
+ coercion = obj.coerce(self)
1077
+ raise TypeError unless coercion.is_a?(Array) && coercion.length == 2
1078
+ coercion[0].public_send(oper, coercion[1])
1079
+ rescue
1080
+ raise TypeError, "#{obj.inspect} can't be coerced into #{self.class}"
1081
+ end
1082
+ private :apply_through_coercion
1083
+
1084
+ #
1085
+ # Helper method to coerce a value into a specific class.
1086
+ # Raises a TypeError if the coercion fails or the returned value
1087
+ # is not of the right class.
1088
+ # (from Rubinius)
1089
+ #
1090
+ def self.coerce_to(obj, cls, meth) # :nodoc:
1091
+ return obj if obj.kind_of?(cls)
1092
+
1093
+ begin
1094
+ ret = obj.__send__(meth)
1095
+ rescue Exception => e
1096
+ raise TypeError, "Coercion error: #{obj.inspect}.#{meth} => #{cls} failed:\n" \
1097
+ "(#{e.message})"
1098
+ end
1099
+ raise TypeError, "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{ret.class})" unless ret.kind_of? cls
1100
+ ret
1101
+ end
1102
+
1103
+ def self.coerce_to_int(obj)
1104
+ coerce_to(obj, Integer, :to_int)
1105
+ end
1106
+ end
1107
+
1108
+ include CoercionHelper
1109
+
1110
+ # Private CLASS
1111
+
1112
+ class Scalar < Numeric # :nodoc:
1113
+ include ExceptionForMatrix
1114
+ include CoercionHelper
1115
+
1116
+ def initialize(value)
1117
+ @value = value
1118
+ end
1119
+
1120
+ # ARITHMETIC
1121
+ def +(other)
1122
+ case other
1123
+ when Numeric
1124
+ Scalar.new(@value + other)
1125
+ when Vector, Matrix
1126
+ Scalar.Raise ErrOperationNotDefined, "+", @value.class, other.class
1127
+ else
1128
+ apply_through_coercion(other, __method__)
1129
+ end
1130
+ end
1131
+
1132
+ def -(other)
1133
+ case other
1134
+ when Numeric
1135
+ Scalar.new(@value - other)
1136
+ when Vector, Matrix
1137
+ Scalar.Raise ErrOperationNotDefined, "-", @value.class, other.class
1138
+ else
1139
+ apply_through_coercion(other, __method__)
1140
+ end
1141
+ end
1142
+
1143
+ def *(other)
1144
+ case other
1145
+ when Numeric
1146
+ Scalar.new(@value * other)
1147
+ when Vector, Matrix
1148
+ other.collect{|e| @value * e}
1149
+ else
1150
+ apply_through_coercion(other, __method__)
1151
+ end
1152
+ end
1153
+
1154
+ def / (other)
1155
+ case other
1156
+ when Numeric
1157
+ Scalar.new(@value / other)
1158
+ when Vector
1159
+ Scalar.Raise ErrOperationNotDefined, "/", @value.class, other.class
1160
+ when Matrix
1161
+ self * other.inverse
1162
+ else
1163
+ apply_through_coercion(other, __method__)
1164
+ end
1165
+ end
1166
+
1167
+ def ** (other)
1168
+ case other
1169
+ when Numeric
1170
+ Scalar.new(@value ** other)
1171
+ when Vector
1172
+ Scalar.Raise ErrOperationNotDefined, "**", @value.class, other.class
1173
+ when Matrix
1174
+ #other.powered_by(self)
1175
+ Scalar.Raise ErrOperationNotImplemented, "**", @value.class, other.class
1176
+ else
1177
+ apply_through_coercion(other, __method__)
1178
+ end
1179
+ end
1180
+ end
1181
+
1182
+ end
1183
+
1184
+
1185
+ #
1186
+ # The +Vector+ class represents a mathematical vector, which is useful in its own right, and
1187
+ # also constitutes a row or column of a Matrix.
1188
+ #
1189
+ # == Method Catalogue
1190
+ #
1191
+ # To create a Vector:
1192
+ # * <tt> Vector.[](*array) </tt>
1193
+ # * <tt> Vector.elements(array, copy = true) </tt>
1194
+ #
1195
+ # To access elements:
1196
+ # * <tt> [](i) </tt>
1197
+ #
1198
+ # To enumerate the elements:
1199
+ # * <tt> #each2(v) </tt>
1200
+ # * <tt> #collect2(v) </tt>
1201
+ #
1202
+ # Vector arithmetic:
1203
+ # * <tt> *(x) "is matrix or number" </tt>
1204
+ # * <tt> +(v) </tt>
1205
+ # * <tt> -(v) </tt>
1206
+ #
1207
+ # 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>
1214
+ #
1215
+ # Conversion to other data types:
1216
+ # * <tt> #covector </tt>
1217
+ # * <tt> #to_a </tt>
1218
+ # * <tt> #coerce(other) </tt>
1219
+ #
1220
+ # String representations:
1221
+ # * <tt> #to_s </tt>
1222
+ # * <tt> #inspect </tt>
1223
+ #
1224
+ class Vector
1225
+ include ExceptionForMatrix
1226
+ include Enumerable
1227
+ include Matrix::CoercionHelper
1228
+ extend Matrix::ConversionHelper
1229
+ #INSTANCE CREATION
1230
+
1231
+ private_class_method :new
1232
+ attr_reader :elements
1233
+ protected :elements
1234
+
1235
+ #
1236
+ # Creates a Vector from a list of elements.
1237
+ # Vector[7, 4, ...]
1238
+ #
1239
+ def Vector.[](*array)
1240
+ new convert_to_array(array, copy = false)
1241
+ end
1242
+
1243
+ #
1244
+ # Creates a vector from an Array. The optional second argument specifies
1245
+ # whether the array itself or a copy is used internally.
1246
+ #
1247
+ def Vector.elements(array, copy = true)
1248
+ new convert_to_array(array, copy)
1249
+ end
1250
+
1251
+ #
1252
+ # Vector.new is private; use Vector[] or Vector.elements to create.
1253
+ #
1254
+ def initialize(array)
1255
+ # No checking is done at this point.
1256
+ @elements = array
1257
+ end
1258
+
1259
+ # ACCESSING
1260
+
1261
+ #
1262
+ # Returns element number +i+ (starting at zero) of the vector.
1263
+ #
1264
+ def [](i)
1265
+ @elements[i]
1266
+ end
1267
+ alias element []
1268
+ alias component []
1269
+
1270
+ def []=(i, v)
1271
+ @elements[i]= v
1272
+ end
1273
+ alias set_element []=
1274
+ alias set_component []=
1275
+ private :[]=, :set_element, :set_component
1276
+
1277
+ #
1278
+ # Returns the number of elements in the vector.
1279
+ #
1280
+ def size
1281
+ @elements.size
1282
+ end
1283
+
1284
+ #--
1285
+ # ENUMERATIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1286
+ #++
1287
+
1288
+ #
1289
+ # Iterate over the elements of this vector
1290
+ #
1291
+ def each(&block)
1292
+ return to_enum(:each) unless block_given?
1293
+ @elements.each(&block)
1294
+ self
1295
+ end
1296
+
1297
+ #
1298
+ # Iterate over the elements of this vector and +v+ in conjunction.
1299
+ #
1300
+ def each2(v) # :yield: e1, e2
1301
+ raise TypeError, "Integer is not like Vector" if v.kind_of?(Integer)
1302
+ Vector.Raise ErrDimensionMismatch if size != v.size
1303
+ return to_enum(:each2, v) unless block_given?
1304
+ size.times do |i|
1305
+ yield @elements[i], v[i]
1306
+ end
1307
+ self
1308
+ end
1309
+
1310
+ #
1311
+ # Collects (as in Enumerable#collect) over the elements of this vector and +v+
1312
+ # in conjunction.
1313
+ #
1314
+ def collect2(v) # :yield: e1, e2
1315
+ raise TypeError, "Integer is not like Vector" if v.kind_of?(Integer)
1316
+ Vector.Raise ErrDimensionMismatch if size != v.size
1317
+ return to_enum(:collect2, v) unless block_given?
1318
+ size.times.collect do |i|
1319
+ yield @elements[i], v[i]
1320
+ end
1321
+ end
1322
+
1323
+ #--
1324
+ # COMPARING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1325
+ #++
1326
+
1327
+ #
1328
+ # Returns +true+ iff the two vectors have the same elements in the same order.
1329
+ #
1330
+ def ==(other)
1331
+ return false unless Vector === other
1332
+ @elements == other.elements
1333
+ end
1334
+
1335
+ def eql?(other)
1336
+ return false unless Vector === other
1337
+ @elements.eql? other.elements
1338
+ end
1339
+
1340
+ #
1341
+ # Return a copy of the vector.
1342
+ #
1343
+ def clone
1344
+ Vector.elements(@elements)
1345
+ end
1346
+
1347
+ #
1348
+ # Return a hash-code for the vector.
1349
+ #
1350
+ def hash
1351
+ @elements.hash
1352
+ end
1353
+
1354
+ #--
1355
+ # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1356
+ #++
1357
+
1358
+ #
1359
+ # Multiplies the vector by +x+, where +x+ is a number or another vector.
1360
+ #
1361
+ def *(x)
1362
+ case x
1363
+ when Numeric
1364
+ els = @elements.collect{|e| e * x}
1365
+ Vector.elements(els, false)
1366
+ when Matrix
1367
+ Matrix.column_vector(self) * x
1368
+ when Vector
1369
+ Vector.Raise ErrOperationNotDefined, "*", self.class, x.class
1370
+ else
1371
+ apply_through_coercion(x, __method__)
1372
+ end
1373
+ end
1374
+
1375
+ #
1376
+ # Vector addition.
1377
+ #
1378
+ def +(v)
1379
+ case v
1380
+ when Vector
1381
+ Vector.Raise ErrDimensionMismatch if size != v.size
1382
+ els = collect2(v) {|v1, v2|
1383
+ v1 + v2
1384
+ }
1385
+ Vector.elements(els, false)
1386
+ when Matrix
1387
+ Matrix.column_vector(self) + v
1388
+ else
1389
+ apply_through_coercion(v, __method__)
1390
+ end
1391
+ end
1392
+
1393
+ #
1394
+ # Vector subtraction.
1395
+ #
1396
+ def -(v)
1397
+ case v
1398
+ when Vector
1399
+ Vector.Raise ErrDimensionMismatch if size != v.size
1400
+ els = collect2(v) {|v1, v2|
1401
+ v1 - v2
1402
+ }
1403
+ Vector.elements(els, false)
1404
+ when Matrix
1405
+ Matrix.column_vector(self) - v
1406
+ else
1407
+ apply_through_coercion(v, __method__)
1408
+ end
1409
+ end
1410
+
1411
+ #
1412
+ # Vector division.
1413
+ #
1414
+ def /(x)
1415
+ case x
1416
+ when Numeric
1417
+ els = @elements.collect{|e| e / x}
1418
+ Vector.elements(els, false)
1419
+ when Matrix, Vector
1420
+ Vector.Raise ErrOperationNotDefined, "/", self.class, x.class
1421
+ else
1422
+ apply_through_coercion(x, __method__)
1423
+ end
1424
+ end
1425
+
1426
+ #--
1427
+ # VECTOR FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1428
+ #++
1429
+
1430
+ #
1431
+ # Returns the inner product of this vector with the other.
1432
+ # Vector[4,7].inner_product Vector[10,1] => 47
1433
+ #
1434
+ def inner_product(v)
1435
+ Vector.Raise ErrDimensionMismatch if size != v.size
1436
+
1437
+ p = 0
1438
+ each2(v) {|v1, v2|
1439
+ p += v1 * v2
1440
+ }
1441
+ p
1442
+ end
1443
+
1444
+ #
1445
+ # Like Array#collect.
1446
+ #
1447
+ def collect(&block) # :yield: e
1448
+ return to_enum(:collect) unless block_given?
1449
+ els = @elements.collect(&block)
1450
+ Vector.elements(els, false)
1451
+ end
1452
+ alias map collect
1453
+
1454
+ #
1455
+ # Like Vector#collect2, but returns a Vector instead of an Array.
1456
+ #
1457
+ def map2(v, &block) # :yield: e1, e2
1458
+ return to_enum(:map2, v) unless block_given?
1459
+ els = collect2(v, &block)
1460
+ Vector.elements(els, false)
1461
+ end
1462
+
1463
+ #
1464
+ # Returns the modulus (Pythagorean distance) of the vector.
1465
+ # Vector[5,8,2].r => 9.643650761
1466
+ #
1467
+ def r
1468
+ Math.sqrt(@elements.inject(0) {|v, e| v + e*e})
1469
+ end
1470
+
1471
+ #--
1472
+ # CONVERTING
1473
+ #++
1474
+
1475
+ #
1476
+ # Creates a single-row matrix from this vector.
1477
+ #
1478
+ def covector
1479
+ Matrix.row_vector(self)
1480
+ end
1481
+
1482
+ #
1483
+ # Returns the elements of the vector in an array.
1484
+ #
1485
+ def to_a
1486
+ @elements.dup
1487
+ end
1488
+
1489
+ def elements_to_f
1490
+ warn "#{caller(1)[0]}: warning: Vector#elements_to_f is deprecated"
1491
+ map(&:to_f)
1492
+ end
1493
+
1494
+ def elements_to_i
1495
+ warn "#{caller(1)[0]}: warning: Vector#elements_to_i is deprecated"
1496
+ map(&:to_i)
1497
+ end
1498
+
1499
+ def elements_to_r
1500
+ warn "#{caller(1)[0]}: warning: Vector#elements_to_r is deprecated"
1501
+ map(&:to_r)
1502
+ end
1503
+
1504
+ #
1505
+ # The coerce method provides support for Ruby type coercion.
1506
+ # This coercion mechanism is used by Ruby to handle mixed-type
1507
+ # numeric operations: it is intended to find a compatible common
1508
+ # type between the two operands of the operator.
1509
+ # See also Numeric#coerce.
1510
+ #
1511
+ def coerce(other)
1512
+ case other
1513
+ when Numeric
1514
+ return Matrix::Scalar.new(other), self
1515
+ else
1516
+ raise TypeError, "#{self.class} can't be coerced into #{other.class}"
1517
+ end
1518
+ end
1519
+
1520
+ #--
1521
+ # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1522
+ #++
1523
+
1524
+ #
1525
+ # Overrides Object#to_s
1526
+ #
1527
+ def to_s
1528
+ "Vector[" + @elements.join(", ") + "]"
1529
+ end
1530
+
1531
+ #
1532
+ # Overrides Object#inspect
1533
+ #
1534
+ def inspect
1535
+ str = "Vector"+@elements.inspect
1536
+ end
1537
+ end