1 | #!/usr/local/bin/ruby
|
---|
2 | #--
|
---|
3 | # matrix.rb -
|
---|
4 | # $Release Version: 1.0$
|
---|
5 | # $Revision: 1.11 $
|
---|
6 | # $Date: 1999/10/06 11:01:53 $
|
---|
7 | # Original Version from Smalltalk-80 version
|
---|
8 | # on July 23, 1985 at 8:37:17 am
|
---|
9 | # by Keiju ISHITSUKA
|
---|
10 | #++
|
---|
11 | #
|
---|
12 | # = matrix.rb
|
---|
13 | #
|
---|
14 | # An implementation of Matrix and Vector classes.
|
---|
15 | #
|
---|
16 | # Author:: Keiju ISHITSUKA
|
---|
17 | # Documentation:: Gavin Sinclair (sourced from <i>Ruby in a Nutshell</i> (Matsumoto, O'Reilly))
|
---|
18 | #
|
---|
19 | # See classes Matrix and Vector for documentation.
|
---|
20 | #
|
---|
21 |
|
---|
22 |
|
---|
23 | require "e2mmap.rb"
|
---|
24 |
|
---|
25 | module ExceptionForMatrix # :nodoc:
|
---|
26 | extend Exception2MessageMapper
|
---|
27 | def_e2message(TypeError, "wrong argument type %s (expected %s)")
|
---|
28 | def_e2message(ArgumentError, "Wrong # of arguments(%d for %d)")
|
---|
29 |
|
---|
30 | def_exception("ErrDimensionMismatch", "\#{self.name} dimension mismatch")
|
---|
31 | def_exception("ErrNotRegular", "Not Regular Matrix")
|
---|
32 | def_exception("ErrOperationNotDefined", "This operation(%s) can\\'t defined")
|
---|
33 | end
|
---|
34 |
|
---|
35 | #
|
---|
36 | # The +Matrix+ class represents a mathematical matrix, and provides methods for creating
|
---|
37 | # special-case matrices (zero, identity, diagonal, singular, vector), operating on them
|
---|
38 | # arithmetically and algebraically, and determining their mathematical properties (trace, rank,
|
---|
39 | # inverse, determinant).
|
---|
40 | #
|
---|
41 | # Note that although matrices should theoretically be rectangular, this is not
|
---|
42 | # enforced by the class.
|
---|
43 | #
|
---|
44 | # Also note that the determinant of integer matrices may be incorrectly calculated unless you
|
---|
45 | # also <tt>require 'mathn'</tt>. This may be fixed in the future.
|
---|
46 | #
|
---|
47 | # == Method Catalogue
|
---|
48 | #
|
---|
49 | # To create a matrix:
|
---|
50 | # * <tt> Matrix[*rows] </tt>
|
---|
51 | # * <tt> Matrix.[](*rows) </tt>
|
---|
52 | # * <tt> Matrix.rows(rows, copy = true) </tt>
|
---|
53 | # * <tt> Matrix.columns(columns) </tt>
|
---|
54 | # * <tt> Matrix.diagonal(*values) </tt>
|
---|
55 | # * <tt> Matrix.scalar(n, value) </tt>
|
---|
56 | # * <tt> Matrix.scalar(n, value) </tt>
|
---|
57 | # * <tt> Matrix.identity(n) </tt>
|
---|
58 | # * <tt> Matrix.unit(n) </tt>
|
---|
59 | # * <tt> Matrix.I(n) </tt>
|
---|
60 | # * <tt> Matrix.zero(n) </tt>
|
---|
61 | # * <tt> Matrix.row_vector(row) </tt>
|
---|
62 | # * <tt> Matrix.column_vector(column) </tt>
|
---|
63 | #
|
---|
64 | # To access Matrix elements/columns/rows/submatrices/properties:
|
---|
65 | # * <tt> [](i, j) </tt>
|
---|
66 | # * <tt> #row_size </tt>
|
---|
67 | # * <tt> #column_size </tt>
|
---|
68 | # * <tt> #row(i) </tt>
|
---|
69 | # * <tt> #column(j) </tt>
|
---|
70 | # * <tt> #collect </tt>
|
---|
71 | # * <tt> #map </tt>
|
---|
72 | # * <tt> #minor(*param) </tt>
|
---|
73 | #
|
---|
74 | # Properties of a matrix:
|
---|
75 | # * <tt> #regular? </tt>
|
---|
76 | # * <tt> #singular? </tt>
|
---|
77 | # * <tt> #square? </tt>
|
---|
78 | #
|
---|
79 | # Matrix arithmetic:
|
---|
80 | # * <tt> *(m) </tt>
|
---|
81 | # * <tt> +(m) </tt>
|
---|
82 | # * <tt> -(m) </tt>
|
---|
83 | # * <tt> #/(m) </tt>
|
---|
84 | # * <tt> #inverse </tt>
|
---|
85 | # * <tt> #inv </tt>
|
---|
86 | # * <tt> ** </tt>
|
---|
87 | #
|
---|
88 | # Matrix functions:
|
---|
89 | # * <tt> #determinant </tt>
|
---|
90 | # * <tt> #det </tt>
|
---|
91 | # * <tt> #rank </tt>
|
---|
92 | # * <tt> #trace </tt>
|
---|
93 | # * <tt> #tr </tt>
|
---|
94 | # * <tt> #transpose </tt>
|
---|
95 | # * <tt> #t </tt>
|
---|
96 | #
|
---|
97 | # Conversion to other data types:
|
---|
98 | # * <tt> #coerce(other) </tt>
|
---|
99 | # * <tt> #row_vectors </tt>
|
---|
100 | # * <tt> #column_vectors </tt>
|
---|
101 | # * <tt> #to_a </tt>
|
---|
102 | #
|
---|
103 | # String representations:
|
---|
104 | # * <tt> #to_s </tt>
|
---|
105 | # * <tt> #inspect </tt>
|
---|
106 | #
|
---|
107 | class Matrix
|
---|
108 | @RCS_ID='-$Id: matrix.rb,v 1.11 1999/10/06 11:01:53 keiju Exp keiju $-'
|
---|
109 |
|
---|
110 | # extend Exception2MessageMapper
|
---|
111 | include ExceptionForMatrix
|
---|
112 |
|
---|
113 | # instance creations
|
---|
114 | private_class_method :new
|
---|
115 |
|
---|
116 | #
|
---|
117 | # Creates a matrix where each argument is a row.
|
---|
118 | # Matrix[ [25, 93], [-1, 66] ]
|
---|
119 | # => 25 93
|
---|
120 | # -1 66
|
---|
121 | #
|
---|
122 | def Matrix.[](*rows)
|
---|
123 | new(:init_rows, rows, false)
|
---|
124 | end
|
---|
125 |
|
---|
126 | #
|
---|
127 | # Creates a matrix where +rows+ is an array of arrays, each of which is a row
|
---|
128 | # to the matrix. If the optional argument +copy+ is false, use the given
|
---|
129 | # arrays as the internal structure of the matrix without copying.
|
---|
130 | # Matrix.rows([[25, 93], [-1, 66]])
|
---|
131 | # => 25 93
|
---|
132 | # -1 66
|
---|
133 | def Matrix.rows(rows, copy = true)
|
---|
134 | new(:init_rows, rows, copy)
|
---|
135 | end
|
---|
136 |
|
---|
137 | #
|
---|
138 | # Creates a matrix using +columns+ as an array of column vectors.
|
---|
139 | # Matrix.columns([[25, 93], [-1, 66]])
|
---|
140 | # => 25 -1
|
---|
141 | # 93 66
|
---|
142 | #
|
---|
143 | #
|
---|
144 | def Matrix.columns(columns)
|
---|
145 | rows = (0 .. columns[0].size - 1).collect {
|
---|
146 | |i|
|
---|
147 | (0 .. columns.size - 1).collect {
|
---|
148 | |j|
|
---|
149 | columns[j][i]
|
---|
150 | }
|
---|
151 | }
|
---|
152 | Matrix.rows(rows, false)
|
---|
153 | end
|
---|
154 |
|
---|
155 | #
|
---|
156 | # Creates a matrix where the diagonal elements are composed of +values+.
|
---|
157 | # Matrix.diagonal(9, 5, -3)
|
---|
158 | # => 9 0 0
|
---|
159 | # 0 5 0
|
---|
160 | # 0 0 -3
|
---|
161 | #
|
---|
162 | def Matrix.diagonal(*values)
|
---|
163 | size = values.size
|
---|
164 | rows = (0 .. size - 1).collect {
|
---|
165 | |j|
|
---|
166 | row = Array.new(size).fill(0, 0, size)
|
---|
167 | row[j] = values[j]
|
---|
168 | row
|
---|
169 | }
|
---|
170 | rows(rows, false)
|
---|
171 | end
|
---|
172 |
|
---|
173 | #
|
---|
174 | # Creates an +n+ by +n+ diagonal matrix where each diagonal element is
|
---|
175 | # +value+.
|
---|
176 | # Matrix.scalar(2, 5)
|
---|
177 | # => 5 0
|
---|
178 | # 0 5
|
---|
179 | #
|
---|
180 | def Matrix.scalar(n, value)
|
---|
181 | Matrix.diagonal(*Array.new(n).fill(value, 0, n))
|
---|
182 | end
|
---|
183 |
|
---|
184 | #
|
---|
185 | # Creates an +n+ by +n+ identity matrix.
|
---|
186 | # Matrix.identity(2)
|
---|
187 | # => 1 0
|
---|
188 | # 0 1
|
---|
189 | #
|
---|
190 | def Matrix.identity(n)
|
---|
191 | Matrix.scalar(n, 1)
|
---|
192 | end
|
---|
193 | class << Matrix
|
---|
194 | alias unit identity
|
---|
195 | alias I identity
|
---|
196 | end
|
---|
197 |
|
---|
198 | #
|
---|
199 | # Creates an +n+ by +n+ zero matrix.
|
---|
200 | # Matrix.zero(2)
|
---|
201 | # => 0 0
|
---|
202 | # 0 0
|
---|
203 | #
|
---|
204 | def Matrix.zero(n)
|
---|
205 | Matrix.scalar(n, 0)
|
---|
206 | end
|
---|
207 |
|
---|
208 | #
|
---|
209 | # Creates a single-row matrix where the values of that row are as given in
|
---|
210 | # +row+.
|
---|
211 | # Matrix.row_vector([4,5,6])
|
---|
212 | # => 4 5 6
|
---|
213 | #
|
---|
214 | def Matrix.row_vector(row)
|
---|
215 | case row
|
---|
216 | when Vector
|
---|
217 | Matrix.rows([row.to_a], false)
|
---|
218 | when Array
|
---|
219 | Matrix.rows([row.dup], false)
|
---|
220 | else
|
---|
221 | Matrix.rows([[row]], false)
|
---|
222 | end
|
---|
223 | end
|
---|
224 |
|
---|
225 | #
|
---|
226 | # Creates a single-column matrix where the values of that column are as given
|
---|
227 | # in +column+.
|
---|
228 | # Matrix.column_vector([4,5,6])
|
---|
229 | # => 4
|
---|
230 | # 5
|
---|
231 | # 6
|
---|
232 | #
|
---|
233 | def Matrix.column_vector(column)
|
---|
234 | case column
|
---|
235 | when Vector
|
---|
236 | Matrix.columns([column.to_a])
|
---|
237 | when Array
|
---|
238 | Matrix.columns([column])
|
---|
239 | else
|
---|
240 | Matrix.columns([[column]])
|
---|
241 | end
|
---|
242 | end
|
---|
243 |
|
---|
244 | #
|
---|
245 | # This method is used by the other methods that create matrices, and is of no
|
---|
246 | # use to general users.
|
---|
247 | #
|
---|
248 | def initialize(init_method, *argv)
|
---|
249 | self.send(init_method, *argv)
|
---|
250 | end
|
---|
251 |
|
---|
252 | def init_rows(rows, copy)
|
---|
253 | if copy
|
---|
254 | @rows = rows.collect{|row| row.dup}
|
---|
255 | else
|
---|
256 | @rows = rows
|
---|
257 | end
|
---|
258 | self
|
---|
259 | end
|
---|
260 | private :init_rows
|
---|
261 |
|
---|
262 | #
|
---|
263 | # Returns element (+i+,+j+) of the matrix. That is: row +i+, column +j+.
|
---|
264 | #
|
---|
265 | def [](i, j)
|
---|
266 | @rows[i][j]
|
---|
267 | end
|
---|
268 |
|
---|
269 | #
|
---|
270 | # Returns the number of rows.
|
---|
271 | #
|
---|
272 | def row_size
|
---|
273 | @rows.size
|
---|
274 | end
|
---|
275 |
|
---|
276 | #
|
---|
277 | # Returns the number of columns. Note that it is possible to construct a
|
---|
278 | # matrix with uneven columns (e.g. Matrix[ [1,2,3], [4,5] ]), but this is
|
---|
279 | # mathematically unsound. This method uses the first row to determine the
|
---|
280 | # result.
|
---|
281 | #
|
---|
282 | def column_size
|
---|
283 | @rows[0].size
|
---|
284 | end
|
---|
285 |
|
---|
286 | #
|
---|
287 | # Returns row vector number +i+ of the matrix as a Vector (starting at 0 like
|
---|
288 | # an array). When a block is given, the elements of that vector are iterated.
|
---|
289 | #
|
---|
290 | def row(i) # :yield: e
|
---|
291 | if block_given?
|
---|
292 | for e in @rows[i]
|
---|
293 | yield e
|
---|
294 | end
|
---|
295 | else
|
---|
296 | Vector.elements(@rows[i])
|
---|
297 | end
|
---|
298 | end
|
---|
299 |
|
---|
300 | #
|
---|
301 | # Returns column vector number +j+ of the matrix as a Vector (starting at 0
|
---|
302 | # like an array). When a block is given, the elements of that vector are
|
---|
303 | # iterated.
|
---|
304 | #
|
---|
305 | def column(j) # :yield: e
|
---|
306 | if block_given?
|
---|
307 | 0.upto(row_size - 1) do
|
---|
308 | |i|
|
---|
309 | yield @rows[i][j]
|
---|
310 | end
|
---|
311 | else
|
---|
312 | col = (0 .. row_size - 1).collect {
|
---|
313 | |i|
|
---|
314 | @rows[i][j]
|
---|
315 | }
|
---|
316 | Vector.elements(col, false)
|
---|
317 | end
|
---|
318 | end
|
---|
319 |
|
---|
320 | #
|
---|
321 | # Returns a matrix that is the result of iteration of the given block over all
|
---|
322 | # elements of the matrix.
|
---|
323 | # Matrix[ [1,2], [3,4] ].collect { |i| i**2 }
|
---|
324 | # => 1 4
|
---|
325 | # 9 16
|
---|
326 | #
|
---|
327 | def collect # :yield: e
|
---|
328 | rows = @rows.collect{|row| row.collect{|e| yield e}}
|
---|
329 | Matrix.rows(rows, false)
|
---|
330 | end
|
---|
331 | alias map collect
|
---|
332 |
|
---|
333 | #
|
---|
334 | # Returns a section of the matrix. The parameters are either:
|
---|
335 | # * start_row, nrows, start_col, ncols; OR
|
---|
336 | # * col_range, row_range
|
---|
337 | #
|
---|
338 | # Matrix.diagonal(9, 5, -3).minor(0..1, 0..2)
|
---|
339 | # => 9 0 0
|
---|
340 | # 0 5 0
|
---|
341 | #
|
---|
342 | def minor(*param)
|
---|
343 | case param.size
|
---|
344 | when 2
|
---|
345 | from_row = param[0].first
|
---|
346 | size_row = param[0].end - from_row
|
---|
347 | size_row += 1 unless param[0].exclude_end?
|
---|
348 | from_col = param[1].first
|
---|
349 | size_col = param[1].end - from_col
|
---|
350 | size_col += 1 unless param[1].exclude_end?
|
---|
351 | when 4
|
---|
352 | from_row = param[0]
|
---|
353 | size_row = param[1]
|
---|
354 | from_col = param[2]
|
---|
355 | size_col = param[3]
|
---|
356 | else
|
---|
357 | Matrix.Raise ArgumentError, param.inspect
|
---|
358 | end
|
---|
359 |
|
---|
360 | rows = @rows[from_row, size_row].collect{
|
---|
361 | |row|
|
---|
362 | row[from_col, size_col]
|
---|
363 | }
|
---|
364 | Matrix.rows(rows, false)
|
---|
365 | end
|
---|
366 |
|
---|
367 | #--
|
---|
368 | # TESTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
369 | #++
|
---|
370 |
|
---|
371 | #
|
---|
372 | # Returns +true+ if this is a regular matrix.
|
---|
373 | #
|
---|
374 | def regular?
|
---|
375 | square? and rank == column_size
|
---|
376 | end
|
---|
377 |
|
---|
378 | #
|
---|
379 | # Returns +true+ is this is a singular (i.e. non-regular) matrix.
|
---|
380 | #
|
---|
381 | def singular?
|
---|
382 | not regular?
|
---|
383 | end
|
---|
384 |
|
---|
385 | #
|
---|
386 | # Returns +true+ is this is a square matrix. See note in column_size about this
|
---|
387 | # being unreliable, though.
|
---|
388 | #
|
---|
389 | def square?
|
---|
390 | column_size == row_size
|
---|
391 | end
|
---|
392 |
|
---|
393 | #--
|
---|
394 | # OBJECT METHODS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
395 | #++
|
---|
396 |
|
---|
397 | #
|
---|
398 | # Returns +true+ if and only if the two matrices contain equal elements.
|
---|
399 | #
|
---|
400 | def ==(other)
|
---|
401 | return false unless Matrix === other
|
---|
402 |
|
---|
403 | other.compare_by_row_vectors(@rows)
|
---|
404 | end
|
---|
405 | alias eql? ==
|
---|
406 |
|
---|
407 | #
|
---|
408 | # Not really intended for general consumption.
|
---|
409 | #
|
---|
410 | def compare_by_row_vectors(rows)
|
---|
411 | return false unless @rows.size == rows.size
|
---|
412 |
|
---|
413 | 0.upto(@rows.size - 1) do
|
---|
414 | |i|
|
---|
415 | return false unless @rows[i] == rows[i]
|
---|
416 | end
|
---|
417 | true
|
---|
418 | end
|
---|
419 |
|
---|
420 | #
|
---|
421 | # Returns a clone of the matrix, so that the contents of each do not reference
|
---|
422 | # identical objects.
|
---|
423 | #
|
---|
424 | def clone
|
---|
425 | Matrix.rows(@rows)
|
---|
426 | end
|
---|
427 |
|
---|
428 | #
|
---|
429 | # Returns a hash-code for the matrix.
|
---|
430 | #
|
---|
431 | def hash
|
---|
432 | value = 0
|
---|
433 | for row in @rows
|
---|
434 | for e in row
|
---|
435 | value ^= e.hash
|
---|
436 | end
|
---|
437 | end
|
---|
438 | return value
|
---|
439 | end
|
---|
440 |
|
---|
441 | #--
|
---|
442 | # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
443 | #++
|
---|
444 |
|
---|
445 | #
|
---|
446 | # Matrix multiplication.
|
---|
447 | # Matrix[[2,4], [6,8]] * Matrix.identity(2)
|
---|
448 | # => 2 4
|
---|
449 | # 6 8
|
---|
450 | #
|
---|
451 | def *(m) # m is matrix or vector or number
|
---|
452 | case(m)
|
---|
453 | when Numeric
|
---|
454 | rows = @rows.collect {
|
---|
455 | |row|
|
---|
456 | row.collect {
|
---|
457 | |e|
|
---|
458 | e * m
|
---|
459 | }
|
---|
460 | }
|
---|
461 | return Matrix.rows(rows, false)
|
---|
462 | when Vector
|
---|
463 | m = Matrix.column_vector(m)
|
---|
464 | r = self * m
|
---|
465 | return r.column(0)
|
---|
466 | when Matrix
|
---|
467 | Matrix.Raise ErrDimensionMismatch if column_size != m.row_size
|
---|
468 |
|
---|
469 | rows = (0 .. row_size - 1).collect {
|
---|
470 | |i|
|
---|
471 | (0 .. m.column_size - 1).collect {
|
---|
472 | |j|
|
---|
473 | vij = 0
|
---|
474 | 0.upto(column_size - 1) do
|
---|
475 | |k|
|
---|
476 | vij += self[i, k] * m[k, j]
|
---|
477 | end
|
---|
478 | vij
|
---|
479 | }
|
---|
480 | }
|
---|
481 | return Matrix.rows(rows, false)
|
---|
482 | else
|
---|
483 | x, y = m.coerce(self)
|
---|
484 | return x * y
|
---|
485 | end
|
---|
486 | end
|
---|
487 |
|
---|
488 | #
|
---|
489 | # Matrix addition.
|
---|
490 | # Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]]
|
---|
491 | # => 6 0
|
---|
492 | # -4 12
|
---|
493 | #
|
---|
494 | def +(m)
|
---|
495 | case m
|
---|
496 | when Numeric
|
---|
497 | Matrix.Raise ErrOperationNotDefined, "+"
|
---|
498 | when Vector
|
---|
499 | m = Matrix.column_vector(m)
|
---|
500 | when Matrix
|
---|
501 | else
|
---|
502 | x, y = m.coerce(self)
|
---|
503 | return x + y
|
---|
504 | end
|
---|
505 |
|
---|
506 | Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
|
---|
507 |
|
---|
508 | rows = (0 .. row_size - 1).collect {
|
---|
509 | |i|
|
---|
510 | (0 .. column_size - 1).collect {
|
---|
511 | |j|
|
---|
512 | self[i, j] + m[i, j]
|
---|
513 | }
|
---|
514 | }
|
---|
515 | Matrix.rows(rows, false)
|
---|
516 | end
|
---|
517 |
|
---|
518 | #
|
---|
519 | # Matrix subtraction.
|
---|
520 | # Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]]
|
---|
521 | # => -8 2
|
---|
522 | # 8 1
|
---|
523 | #
|
---|
524 | def -(m)
|
---|
525 | case m
|
---|
526 | when Numeric
|
---|
527 | Matrix.Raise ErrOperationNotDefined, "-"
|
---|
528 | when Vector
|
---|
529 | m = Matrix.column_vector(m)
|
---|
530 | when Matrix
|
---|
531 | else
|
---|
532 | x, y = m.coerce(self)
|
---|
533 | return x - y
|
---|
534 | end
|
---|
535 |
|
---|
536 | Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
|
---|
537 |
|
---|
538 | rows = (0 .. row_size - 1).collect {
|
---|
539 | |i|
|
---|
540 | (0 .. column_size - 1).collect {
|
---|
541 | |j|
|
---|
542 | self[i, j] - m[i, j]
|
---|
543 | }
|
---|
544 | }
|
---|
545 | Matrix.rows(rows, false)
|
---|
546 | end
|
---|
547 |
|
---|
548 | #
|
---|
549 | # Matrix division (multiplication by the inverse).
|
---|
550 | # Matrix[[7,6], [3,9]] / Matrix[[2,9], [3,1]]
|
---|
551 | # => -7 1
|
---|
552 | # -3 -6
|
---|
553 | #
|
---|
554 | def /(other)
|
---|
555 | case other
|
---|
556 | when Numeric
|
---|
557 | rows = @rows.collect {
|
---|
558 | |row|
|
---|
559 | row.collect {
|
---|
560 | |e|
|
---|
561 | e / other
|
---|
562 | }
|
---|
563 | }
|
---|
564 | return Matrix.rows(rows, false)
|
---|
565 | when Matrix
|
---|
566 | return self * other.inverse
|
---|
567 | else
|
---|
568 | x, y = other.coerce(self)
|
---|
569 | rerurn x / y
|
---|
570 | end
|
---|
571 | end
|
---|
572 |
|
---|
573 | #
|
---|
574 | # Returns the inverse of the matrix.
|
---|
575 | # Matrix[[1, 2], [2, 1]].inverse
|
---|
576 | # => -1 1
|
---|
577 | # 0 -1
|
---|
578 | #
|
---|
579 | def inverse
|
---|
580 | Matrix.Raise ErrDimensionMismatch unless square?
|
---|
581 | Matrix.I(row_size).inverse_from(self)
|
---|
582 | end
|
---|
583 | alias inv inverse
|
---|
584 |
|
---|
585 | #
|
---|
586 | # Not for public consumption?
|
---|
587 | #
|
---|
588 | def inverse_from(src)
|
---|
589 | size = row_size - 1
|
---|
590 | a = src.to_a
|
---|
591 |
|
---|
592 | for k in 0..size
|
---|
593 | if (akk = a[k][k]) == 0
|
---|
594 | i = k
|
---|
595 | begin
|
---|
596 | Matrix.Raise ErrNotRegular if (i += 1) > size
|
---|
597 | end while a[i][k] == 0
|
---|
598 | a[i], a[k] = a[k], a[i]
|
---|
599 | @rows[i], @rows[k] = @rows[k], @rows[i]
|
---|
600 | akk = a[k][k]
|
---|
601 | end
|
---|
602 |
|
---|
603 | for i in 0 .. size
|
---|
604 | next if i == k
|
---|
605 | q = a[i][k] / akk
|
---|
606 | a[i][k] = 0
|
---|
607 |
|
---|
608 | (k + 1).upto(size) do
|
---|
609 | |j|
|
---|
610 | a[i][j] -= a[k][j] * q
|
---|
611 | end
|
---|
612 | 0.upto(size) do
|
---|
613 | |j|
|
---|
614 | @rows[i][j] -= @rows[k][j] * q
|
---|
615 | end
|
---|
616 | end
|
---|
617 |
|
---|
618 | (k + 1).upto(size) do
|
---|
619 | |j|
|
---|
620 | a[k][j] /= akk
|
---|
621 | end
|
---|
622 | 0.upto(size) do
|
---|
623 | |j|
|
---|
624 | @rows[k][j] /= akk
|
---|
625 | end
|
---|
626 | end
|
---|
627 | self
|
---|
628 | end
|
---|
629 | #alias reciprocal inverse
|
---|
630 |
|
---|
631 | #
|
---|
632 | # Matrix exponentiation. Defined for integer powers only. Equivalent to
|
---|
633 | # multiplying the matrix by itself N times.
|
---|
634 | # Matrix[[7,6], [3,9]] ** 2
|
---|
635 | # => 67 96
|
---|
636 | # 48 99
|
---|
637 | #
|
---|
638 | def ** (other)
|
---|
639 | if other.kind_of?(Integer)
|
---|
640 | x = self
|
---|
641 | if other <= 0
|
---|
642 | x = self.inverse
|
---|
643 | return Matrix.identity(self.column_size) if other == 0
|
---|
644 | other = -other
|
---|
645 | end
|
---|
646 | z = x
|
---|
647 | n = other - 1
|
---|
648 | while n != 0
|
---|
649 | while (div, mod = n.divmod(2)
|
---|
650 | mod == 0)
|
---|
651 | x = x * x
|
---|
652 | n = div
|
---|
653 | end
|
---|
654 | z *= x
|
---|
655 | n -= 1
|
---|
656 | end
|
---|
657 | z
|
---|
658 | elsif other.kind_of?(Float) || defined?(Rational) && other.kind_of?(Rational)
|
---|
659 | Matrix.Raise ErrOperationNotDefined, "**"
|
---|
660 | else
|
---|
661 | Matrix.Raise ErrOperationNotDefined, "**"
|
---|
662 | end
|
---|
663 | end
|
---|
664 |
|
---|
665 | #--
|
---|
666 | # MATRIX FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
667 | #++
|
---|
668 |
|
---|
669 | #
|
---|
670 | # Returns the determinant of the matrix. If the matrix is not square, the
|
---|
671 | # result is 0.
|
---|
672 | # Matrix[[7,6], [3,9]].determinant
|
---|
673 | # => 63
|
---|
674 | #
|
---|
675 | def determinant
|
---|
676 | return 0 unless square?
|
---|
677 |
|
---|
678 | size = row_size - 1
|
---|
679 | a = to_a
|
---|
680 |
|
---|
681 | det = 1
|
---|
682 | k = 0
|
---|
683 | begin
|
---|
684 | if (akk = a[k][k]) == 0
|
---|
685 | i = k
|
---|
686 | begin
|
---|
687 | return 0 if (i += 1) > size
|
---|
688 | end while a[i][k] == 0
|
---|
689 | a[i], a[k] = a[k], a[i]
|
---|
690 | akk = a[k][k]
|
---|
691 | det *= -1
|
---|
692 | end
|
---|
693 | (k + 1).upto(size) do
|
---|
694 | |i|
|
---|
695 | q = a[i][k] / akk
|
---|
696 | (k + 1).upto(size) do
|
---|
697 | |j|
|
---|
698 | a[i][j] -= a[k][j] * q
|
---|
699 | end
|
---|
700 | end
|
---|
701 | det *= akk
|
---|
702 | end while (k += 1) <= size
|
---|
703 | det
|
---|
704 | end
|
---|
705 | alias det determinant
|
---|
706 |
|
---|
707 | #
|
---|
708 | # Returns the rank of the matrix. Beware that using Float values, with their
|
---|
709 | # usual lack of precision, can affect the value returned by this method. Use
|
---|
710 | # Rational values instead if this is important to you.
|
---|
711 | # Matrix[[7,6], [3,9]].rank
|
---|
712 | # => 2
|
---|
713 | #
|
---|
714 | def rank
|
---|
715 | if column_size > row_size
|
---|
716 | a = transpose.to_a
|
---|
717 | a_column_size = row_size
|
---|
718 | a_row_size = column_size
|
---|
719 | else
|
---|
720 | a = to_a
|
---|
721 | a_column_size = column_size
|
---|
722 | a_row_size = row_size
|
---|
723 | end
|
---|
724 | rank = 0
|
---|
725 | k = 0
|
---|
726 | begin
|
---|
727 | if (akk = a[k][k]) == 0
|
---|
728 | i = k
|
---|
729 | exists = true
|
---|
730 | begin
|
---|
731 | if (i += 1) > a_column_size - 1
|
---|
732 | exists = false
|
---|
733 | break
|
---|
734 | end
|
---|
735 | end while a[i][k] == 0
|
---|
736 | if exists
|
---|
737 | a[i], a[k] = a[k], a[i]
|
---|
738 | akk = a[k][k]
|
---|
739 | else
|
---|
740 | i = k
|
---|
741 | exists = true
|
---|
742 | begin
|
---|
743 | if (i += 1) > a_row_size - 1
|
---|
744 | exists = false
|
---|
745 | break
|
---|
746 | end
|
---|
747 | end while a[k][i] == 0
|
---|
748 | if exists
|
---|
749 | k.upto(a_column_size - 1) do
|
---|
750 | |j|
|
---|
751 | a[j][k], a[j][i] = a[j][i], a[j][k]
|
---|
752 | end
|
---|
753 | akk = a[k][k]
|
---|
754 | else
|
---|
755 | next
|
---|
756 | end
|
---|
757 | end
|
---|
758 | end
|
---|
759 | (k + 1).upto(a_row_size - 1) do
|
---|
760 | |i|
|
---|
761 | q = a[i][k] / akk
|
---|
762 | (k + 1).upto(a_column_size - 1) do
|
---|
763 | |j|
|
---|
764 | a[i][j] -= a[k][j] * q
|
---|
765 | end
|
---|
766 | end
|
---|
767 | rank += 1
|
---|
768 | end while (k += 1) <= a_column_size - 1
|
---|
769 | return rank
|
---|
770 | end
|
---|
771 |
|
---|
772 | #
|
---|
773 | # Returns the trace (sum of diagonal elements) of the matrix.
|
---|
774 | # Matrix[[7,6], [3,9]].trace
|
---|
775 | # => 16
|
---|
776 | #
|
---|
777 | def trace
|
---|
778 | tr = 0
|
---|
779 | 0.upto(column_size - 1) do
|
---|
780 | |i|
|
---|
781 | tr += @rows[i][i]
|
---|
782 | end
|
---|
783 | tr
|
---|
784 | end
|
---|
785 | alias tr trace
|
---|
786 |
|
---|
787 | #
|
---|
788 | # Returns the transpose of the matrix.
|
---|
789 | # Matrix[[1,2], [3,4], [5,6]]
|
---|
790 | # => 1 2
|
---|
791 | # 3 4
|
---|
792 | # 5 6
|
---|
793 | # Matrix[[1,2], [3,4], [5,6]].transpose
|
---|
794 | # => 1 3 5
|
---|
795 | # 2 4 6
|
---|
796 | #
|
---|
797 | def transpose
|
---|
798 | Matrix.columns(@rows)
|
---|
799 | end
|
---|
800 | alias t transpose
|
---|
801 |
|
---|
802 | #--
|
---|
803 | # CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
804 | #++
|
---|
805 |
|
---|
806 | #
|
---|
807 | # FIXME: describe #coerce.
|
---|
808 | #
|
---|
809 | def coerce(other)
|
---|
810 | case other
|
---|
811 | when Numeric
|
---|
812 | return Scalar.new(other), self
|
---|
813 | else
|
---|
814 | raise TypeError, "#{self.class} can't be coerced into #{other.class}"
|
---|
815 | end
|
---|
816 | end
|
---|
817 |
|
---|
818 | #
|
---|
819 | # Returns an array of the row vectors of the matrix. See Vector.
|
---|
820 | #
|
---|
821 | def row_vectors
|
---|
822 | rows = (0 .. row_size - 1).collect {
|
---|
823 | |i|
|
---|
824 | row(i)
|
---|
825 | }
|
---|
826 | rows
|
---|
827 | end
|
---|
828 |
|
---|
829 | #
|
---|
830 | # Returns an array of the column vectors of the matrix. See Vector.
|
---|
831 | #
|
---|
832 | def column_vectors
|
---|
833 | columns = (0 .. column_size - 1).collect {
|
---|
834 | |i|
|
---|
835 | column(i)
|
---|
836 | }
|
---|
837 | columns
|
---|
838 | end
|
---|
839 |
|
---|
840 | #
|
---|
841 | # Returns an array of arrays that describe the rows of the matrix.
|
---|
842 | #
|
---|
843 | def to_a
|
---|
844 | @rows.collect{|row| row.collect{|e| e}}
|
---|
845 | end
|
---|
846 |
|
---|
847 | #--
|
---|
848 | # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
849 | #++
|
---|
850 |
|
---|
851 | #
|
---|
852 | # Overrides Object#to_s
|
---|
853 | #
|
---|
854 | def to_s
|
---|
855 | "Matrix[" + @rows.collect{
|
---|
856 | |row|
|
---|
857 | "[" + row.collect{|e| e.to_s}.join(", ") + "]"
|
---|
858 | }.join(", ")+"]"
|
---|
859 | end
|
---|
860 |
|
---|
861 | #
|
---|
862 | # Overrides Object#inspect
|
---|
863 | #
|
---|
864 | def inspect
|
---|
865 | "Matrix"[email protected]
|
---|
866 | end
|
---|
867 |
|
---|
868 | # Private CLASS
|
---|
869 |
|
---|
870 | class Scalar < Numeric # :nodoc:
|
---|
871 | include ExceptionForMatrix
|
---|
872 |
|
---|
873 | def initialize(value)
|
---|
874 | @value = value
|
---|
875 | end
|
---|
876 |
|
---|
877 | # ARITHMETIC
|
---|
878 | def +(other)
|
---|
879 | case other
|
---|
880 | when Numeric
|
---|
881 | Scalar.new(@value + other)
|
---|
882 | when Vector, Matrix
|
---|
883 | Scalar.Raise WrongArgType, other.class, "Numeric or Scalar"
|
---|
884 | when Scalar
|
---|
885 | Scalar.new(@value + other.value)
|
---|
886 | else
|
---|
887 | x, y = other.coerce(self)
|
---|
888 | x + y
|
---|
889 | end
|
---|
890 | end
|
---|
891 |
|
---|
892 | def -(other)
|
---|
893 | case other
|
---|
894 | when Numeric
|
---|
895 | Scalar.new(@value - other)
|
---|
896 | when Vector, Matrix
|
---|
897 | Scalar.Raise WrongArgType, other.class, "Numeric or Scalar"
|
---|
898 | when Scalar
|
---|
899 | Scalar.new(@value - other.value)
|
---|
900 | else
|
---|
901 | x, y = other.coerce(self)
|
---|
902 | x - y
|
---|
903 | end
|
---|
904 | end
|
---|
905 |
|
---|
906 | def *(other)
|
---|
907 | case other
|
---|
908 | when Numeric
|
---|
909 | Scalar.new(@value * other)
|
---|
910 | when Vector, Matrix
|
---|
911 | other.collect{|e| @value * e}
|
---|
912 | else
|
---|
913 | x, y = other.coerce(self)
|
---|
914 | x * y
|
---|
915 | end
|
---|
916 | end
|
---|
917 |
|
---|
918 | def / (other)
|
---|
919 | case other
|
---|
920 | when Numeric
|
---|
921 | Scalar.new(@value / other)
|
---|
922 | when Vector
|
---|
923 | Scalar.Raise WrongArgType, other.class, "Numeric or Scalar or Matrix"
|
---|
924 | when Matrix
|
---|
925 | self * _M.inverse
|
---|
926 | else
|
---|
927 | x, y = other.coerce(self)
|
---|
928 | x / y
|
---|
929 | end
|
---|
930 | end
|
---|
931 |
|
---|
932 | def ** (other)
|
---|
933 | case other
|
---|
934 | when Numeric
|
---|
935 | Scalar.new(@value ** other)
|
---|
936 | when Vector
|
---|
937 | Scalar.Raise WrongArgType, other.class, "Numeric or Scalar or Matrix"
|
---|
938 | when Matrix
|
---|
939 | other.powered_by(self)
|
---|
940 | else
|
---|
941 | x, y = other.coerce(self)
|
---|
942 | x ** y
|
---|
943 | end
|
---|
944 | end
|
---|
945 | end
|
---|
946 | end
|
---|
947 |
|
---|
948 |
|
---|
949 | #
|
---|
950 | # The +Vector+ class represents a mathematical vector, which is useful in its own right, and
|
---|
951 | # also constitutes a row or column of a Matrix.
|
---|
952 | #
|
---|
953 | # == Method Catalogue
|
---|
954 | #
|
---|
955 | # To create a Vector:
|
---|
956 | # * <tt> Vector.[](*array) </tt>
|
---|
957 | # * <tt> Vector.elements(array, copy = true) </tt>
|
---|
958 | #
|
---|
959 | # To access elements:
|
---|
960 | # * <tt> [](i) </tt>
|
---|
961 | #
|
---|
962 | # To enumerate the elements:
|
---|
963 | # * <tt> #each2(v) </tt>
|
---|
964 | # * <tt> #collect2(v) </tt>
|
---|
965 | #
|
---|
966 | # Vector arithmetic:
|
---|
967 | # * <tt> *(x) "is matrix or number" </tt>
|
---|
968 | # * <tt> +(v) </tt>
|
---|
969 | # * <tt> -(v) </tt>
|
---|
970 | #
|
---|
971 | # Vector functions:
|
---|
972 | # * <tt> #inner_product(v) </tt>
|
---|
973 | # * <tt> #collect </tt>
|
---|
974 | # * <tt> #map </tt>
|
---|
975 | # * <tt> #map2(v) </tt>
|
---|
976 | # * <tt> #r </tt>
|
---|
977 | # * <tt> #size </tt>
|
---|
978 | #
|
---|
979 | # Conversion to other data types:
|
---|
980 | # * <tt> #covector </tt>
|
---|
981 | # * <tt> #to_a </tt>
|
---|
982 | # * <tt> #coerce(other) </tt>
|
---|
983 | #
|
---|
984 | # String representations:
|
---|
985 | # * <tt> #to_s </tt>
|
---|
986 | # * <tt> #inspect </tt>
|
---|
987 | #
|
---|
988 | class Vector
|
---|
989 | include ExceptionForMatrix
|
---|
990 |
|
---|
991 | #INSTANCE CREATION
|
---|
992 |
|
---|
993 | private_class_method :new
|
---|
994 |
|
---|
995 | #
|
---|
996 | # Creates a Vector from a list of elements.
|
---|
997 | # Vector[7, 4, ...]
|
---|
998 | #
|
---|
999 | def Vector.[](*array)
|
---|
1000 | new(:init_elements, array, copy = false)
|
---|
1001 | end
|
---|
1002 |
|
---|
1003 | #
|
---|
1004 | # Creates a vector from an Array. The optional second argument specifies
|
---|
1005 | # whether the array itself or a copy is used internally.
|
---|
1006 | #
|
---|
1007 | def Vector.elements(array, copy = true)
|
---|
1008 | new(:init_elements, array, copy)
|
---|
1009 | end
|
---|
1010 |
|
---|
1011 | #
|
---|
1012 | # For internal use.
|
---|
1013 | #
|
---|
1014 | def initialize(method, array, copy)
|
---|
1015 | self.send(method, array, copy)
|
---|
1016 | end
|
---|
1017 |
|
---|
1018 | #
|
---|
1019 | # For internal use.
|
---|
1020 | #
|
---|
1021 | def init_elements(array, copy)
|
---|
1022 | if copy
|
---|
1023 | @elements = array.dup
|
---|
1024 | else
|
---|
1025 | @elements = array
|
---|
1026 | end
|
---|
1027 | end
|
---|
1028 |
|
---|
1029 | # ACCESSING
|
---|
1030 |
|
---|
1031 | #
|
---|
1032 | # Returns element number +i+ (starting at zero) of the vector.
|
---|
1033 | #
|
---|
1034 | def [](i)
|
---|
1035 | @elements[i]
|
---|
1036 | end
|
---|
1037 |
|
---|
1038 | #
|
---|
1039 | # Returns the number of elements in the vector.
|
---|
1040 | #
|
---|
1041 | def size
|
---|
1042 | @elements.size
|
---|
1043 | end
|
---|
1044 |
|
---|
1045 | #--
|
---|
1046 | # ENUMERATIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
1047 | #++
|
---|
1048 |
|
---|
1049 | #
|
---|
1050 | # Iterate over the elements of this vector and +v+ in conjunction.
|
---|
1051 | #
|
---|
1052 | def each2(v) # :yield: e1, e2
|
---|
1053 | Vector.Raise ErrDimensionMismatch if size != v.size
|
---|
1054 | 0.upto(size - 1) do
|
---|
1055 | |i|
|
---|
1056 | yield @elements[i], v[i]
|
---|
1057 | end
|
---|
1058 | end
|
---|
1059 |
|
---|
1060 | #
|
---|
1061 | # Collects (as in Enumerable#collect) over the elements of this vector and +v+
|
---|
1062 | # in conjunction.
|
---|
1063 | #
|
---|
1064 | def collect2(v) # :yield: e1, e2
|
---|
1065 | Vector.Raise ErrDimensionMismatch if size != v.size
|
---|
1066 | (0 .. size - 1).collect do
|
---|
1067 | |i|
|
---|
1068 | yield @elements[i], v[i]
|
---|
1069 | end
|
---|
1070 | end
|
---|
1071 |
|
---|
1072 | #--
|
---|
1073 | # COMPARING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
1074 | #++
|
---|
1075 |
|
---|
1076 | #
|
---|
1077 | # Returns +true+ iff the two vectors have the same elements in the same order.
|
---|
1078 | #
|
---|
1079 | def ==(other)
|
---|
1080 | return false unless Vector === other
|
---|
1081 |
|
---|
1082 | other.compare_by(@elements)
|
---|
1083 | end
|
---|
1084 | alias eqn? ==
|
---|
1085 |
|
---|
1086 | #
|
---|
1087 | # For internal use.
|
---|
1088 | #
|
---|
1089 | def compare_by(elements)
|
---|
1090 | @elements == elements
|
---|
1091 | end
|
---|
1092 |
|
---|
1093 | #
|
---|
1094 | # Return a copy of the vector.
|
---|
1095 | #
|
---|
1096 | def clone
|
---|
1097 | Vector.elements(@elements)
|
---|
1098 | end
|
---|
1099 |
|
---|
1100 | #
|
---|
1101 | # Return a hash-code for the vector.
|
---|
1102 | #
|
---|
1103 | def hash
|
---|
1104 | @elements.hash
|
---|
1105 | end
|
---|
1106 |
|
---|
1107 | #--
|
---|
1108 | # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
1109 | #++
|
---|
1110 |
|
---|
1111 | #
|
---|
1112 | # Multiplies the vector by +x+, where +x+ is a number or another vector.
|
---|
1113 | #
|
---|
1114 | def *(x)
|
---|
1115 | case x
|
---|
1116 | when Numeric
|
---|
1117 | els = @elements.collect{|e| e * x}
|
---|
1118 | Vector.elements(els, false)
|
---|
1119 | when Matrix
|
---|
1120 | Matrix.column_vector(self) * x
|
---|
1121 | else
|
---|
1122 | s, x = x.coerce(self)
|
---|
1123 | s * x
|
---|
1124 | end
|
---|
1125 | end
|
---|
1126 |
|
---|
1127 | #
|
---|
1128 | # Vector addition.
|
---|
1129 | #
|
---|
1130 | def +(v)
|
---|
1131 | case v
|
---|
1132 | when Vector
|
---|
1133 | Vector.Raise ErrDimensionMismatch if size != v.size
|
---|
1134 | els = collect2(v) {
|
---|
1135 | |v1, v2|
|
---|
1136 | v1 + v2
|
---|
1137 | }
|
---|
1138 | Vector.elements(els, false)
|
---|
1139 | when Matrix
|
---|
1140 | Matrix.column_vector(self) + v
|
---|
1141 | else
|
---|
1142 | s, x = v.coerce(self)
|
---|
1143 | s + x
|
---|
1144 | end
|
---|
1145 | end
|
---|
1146 |
|
---|
1147 | #
|
---|
1148 | # Vector subtraction.
|
---|
1149 | #
|
---|
1150 | def -(v)
|
---|
1151 | case v
|
---|
1152 | when Vector
|
---|
1153 | Vector.Raise ErrDimensionMismatch if size != v.size
|
---|
1154 | els = collect2(v) {
|
---|
1155 | |v1, v2|
|
---|
1156 | v1 - v2
|
---|
1157 | }
|
---|
1158 | Vector.elements(els, false)
|
---|
1159 | when Matrix
|
---|
1160 | Matrix.column_vector(self) - v
|
---|
1161 | else
|
---|
1162 | s, x = v.coerce(self)
|
---|
1163 | s - x
|
---|
1164 | end
|
---|
1165 | end
|
---|
1166 |
|
---|
1167 | #--
|
---|
1168 | # VECTOR FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
1169 | #++
|
---|
1170 |
|
---|
1171 | #
|
---|
1172 | # Returns the inner product of this vector with the other.
|
---|
1173 | # Vector[4,7].inner_product Vector[10,1] => 47
|
---|
1174 | #
|
---|
1175 | def inner_product(v)
|
---|
1176 | Vector.Raise ErrDimensionMismatch if size != v.size
|
---|
1177 |
|
---|
1178 | p = 0
|
---|
1179 | each2(v) {
|
---|
1180 | |v1, v2|
|
---|
1181 | p += v1 * v2
|
---|
1182 | }
|
---|
1183 | p
|
---|
1184 | end
|
---|
1185 |
|
---|
1186 | #
|
---|
1187 | # Like Array#collect.
|
---|
1188 | #
|
---|
1189 | def collect # :yield: e
|
---|
1190 | els = @elements.collect {
|
---|
1191 | |v|
|
---|
1192 | yield v
|
---|
1193 | }
|
---|
1194 | Vector.elements(els, false)
|
---|
1195 | end
|
---|
1196 | alias map collect
|
---|
1197 |
|
---|
1198 | #
|
---|
1199 | # Like Vector#collect2, but returns a Vector instead of an Array.
|
---|
1200 | #
|
---|
1201 | def map2(v) # :yield: e1, e2
|
---|
1202 | els = collect2(v) {
|
---|
1203 | |v1, v2|
|
---|
1204 | yield v1, v2
|
---|
1205 | }
|
---|
1206 | Vector.elements(els, false)
|
---|
1207 | end
|
---|
1208 |
|
---|
1209 | #
|
---|
1210 | # Returns the modulus (Pythagorean distance) of the vector.
|
---|
1211 | # Vector[5,8,2].r => 9.643650761
|
---|
1212 | #
|
---|
1213 | def r
|
---|
1214 | v = 0
|
---|
1215 | for e in @elements
|
---|
1216 | v += e*e
|
---|
1217 | end
|
---|
1218 | return Math.sqrt(v)
|
---|
1219 | end
|
---|
1220 |
|
---|
1221 | #--
|
---|
1222 | # CONVERTING
|
---|
1223 | #++
|
---|
1224 |
|
---|
1225 | #
|
---|
1226 | # Creates a single-row matrix from this vector.
|
---|
1227 | #
|
---|
1228 | def covector
|
---|
1229 | Matrix.row_vector(self)
|
---|
1230 | end
|
---|
1231 |
|
---|
1232 | #
|
---|
1233 | # Returns the elements of the vector in an array.
|
---|
1234 | #
|
---|
1235 | def to_a
|
---|
1236 | @elements.dup
|
---|
1237 | end
|
---|
1238 |
|
---|
1239 | #
|
---|
1240 | # FIXME: describe Vector#coerce.
|
---|
1241 | #
|
---|
1242 | def coerce(other)
|
---|
1243 | case other
|
---|
1244 | when Numeric
|
---|
1245 | return Scalar.new(other), self
|
---|
1246 | else
|
---|
1247 | raise TypeError, "#{self.class} can't be coerced into #{other.class}"
|
---|
1248 | end
|
---|
1249 | end
|
---|
1250 |
|
---|
1251 | #--
|
---|
1252 | # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
---|
1253 | #++
|
---|
1254 |
|
---|
1255 | #
|
---|
1256 | # Overrides Object#to_s
|
---|
1257 | #
|
---|
1258 | def to_s
|
---|
1259 | "Vector[" + @elements.join(", ") + "]"
|
---|
1260 | end
|
---|
1261 |
|
---|
1262 | #
|
---|
1263 | # Overrides Object#inspect
|
---|
1264 | #
|
---|
1265 | def inspect
|
---|
1266 | str = "Vector"[email protected]
|
---|
1267 | end
|
---|
1268 | end
|
---|
1269 |
|
---|
1270 |
|
---|
1271 | # Documentation comments:
|
---|
1272 | # - Matrix#coerce and Vector#coerce need to be documented
|
---|