rubysl-matrix 1.0.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 (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