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