nmatrix 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +5 -0
- data/History.txt +97 -0
- data/Manifest.txt +34 -7
- data/README.rdoc +13 -13
- data/Rakefile +36 -26
- data/ext/nmatrix/data/data.cpp +15 -2
- data/ext/nmatrix/data/data.h +4 -0
- data/ext/nmatrix/data/ruby_object.h +5 -14
- data/ext/nmatrix/extconf.rb +3 -2
- data/ext/nmatrix/{util/math.cpp → math.cpp} +296 -6
- data/ext/nmatrix/math/asum.h +143 -0
- data/ext/nmatrix/math/geev.h +82 -0
- data/ext/nmatrix/math/gemm.h +267 -0
- data/ext/nmatrix/math/gemv.h +208 -0
- data/ext/nmatrix/math/ger.h +96 -0
- data/ext/nmatrix/math/gesdd.h +80 -0
- data/ext/nmatrix/math/gesvd.h +78 -0
- data/ext/nmatrix/math/getf2.h +86 -0
- data/ext/nmatrix/math/getrf.h +240 -0
- data/ext/nmatrix/math/getri.h +107 -0
- data/ext/nmatrix/math/getrs.h +125 -0
- data/ext/nmatrix/math/idamax.h +86 -0
- data/ext/nmatrix/{util → math}/lapack.h +60 -356
- data/ext/nmatrix/math/laswp.h +165 -0
- data/ext/nmatrix/math/long_dtype.h +52 -0
- data/ext/nmatrix/math/math.h +1154 -0
- data/ext/nmatrix/math/nrm2.h +181 -0
- data/ext/nmatrix/math/potrs.h +125 -0
- data/ext/nmatrix/math/rot.h +141 -0
- data/ext/nmatrix/math/rotg.h +115 -0
- data/ext/nmatrix/math/scal.h +73 -0
- data/ext/nmatrix/math/swap.h +73 -0
- data/ext/nmatrix/math/trsm.h +383 -0
- data/ext/nmatrix/nmatrix.cpp +176 -152
- data/ext/nmatrix/nmatrix.h +1 -2
- data/ext/nmatrix/ruby_constants.cpp +9 -4
- data/ext/nmatrix/ruby_constants.h +1 -0
- data/ext/nmatrix/storage/dense.cpp +57 -41
- data/ext/nmatrix/storage/list.cpp +52 -50
- data/ext/nmatrix/storage/storage.cpp +59 -43
- data/ext/nmatrix/storage/yale.cpp +352 -333
- data/ext/nmatrix/storage/yale.h +4 -0
- data/lib/nmatrix.rb +2 -2
- data/lib/nmatrix/blas.rb +4 -4
- data/lib/nmatrix/enumerate.rb +241 -0
- data/lib/nmatrix/lapack.rb +54 -1
- data/lib/nmatrix/math.rb +462 -0
- data/lib/nmatrix/nmatrix.rb +210 -486
- data/lib/nmatrix/nvector.rb +0 -62
- data/lib/nmatrix/rspec.rb +75 -0
- data/lib/nmatrix/shortcuts.rb +136 -108
- data/lib/nmatrix/version.rb +1 -1
- data/spec/blas_spec.rb +20 -12
- data/spec/elementwise_spec.rb +22 -13
- data/spec/io_spec.rb +1 -0
- data/spec/lapack_spec.rb +197 -0
- data/spec/nmatrix_spec.rb +39 -38
- data/spec/nvector_spec.rb +3 -9
- data/spec/rspec_monkeys.rb +29 -0
- data/spec/rspec_spec.rb +34 -0
- data/spec/shortcuts_spec.rb +14 -16
- data/spec/slice_spec.rb +242 -186
- data/spec/spec_helper.rb +19 -0
- metadata +33 -5
- data/ext/nmatrix/util/math.h +0 -2612
data/lib/nmatrix/nmatrix.rb
CHANGED
@@ -23,15 +23,16 @@
|
|
23
23
|
#
|
24
24
|
# == nmatrix.rb
|
25
25
|
#
|
26
|
-
# This file
|
27
|
-
#
|
26
|
+
# This file contains those core functionalities which can be
|
27
|
+
# implemented efficiently (or much more easily) in Ruby (e.g.,
|
28
|
+
# inspect, pretty_print, element-wise operations).
|
28
29
|
#++
|
29
30
|
|
30
|
-
require_relative './shortcuts.rb'
|
31
31
|
require_relative './lapack.rb'
|
32
32
|
require_relative './yale_functions.rb'
|
33
33
|
|
34
34
|
class NMatrix
|
35
|
+
|
35
36
|
# Read and write extensions for NMatrix. These are only loaded when needed.
|
36
37
|
#
|
37
38
|
module IO
|
@@ -51,36 +52,61 @@ class NMatrix
|
|
51
52
|
autoload :Market, 'nmatrix/io/market'
|
52
53
|
end
|
53
54
|
|
55
|
+
class << self
|
56
|
+
#
|
57
|
+
# call-seq:
|
58
|
+
# load_file(path) -> Mat5Reader
|
59
|
+
#
|
60
|
+
# * *Arguments* :
|
61
|
+
# - +path+ -> The path to a version 5 .mat file.
|
62
|
+
# * *Returns* :
|
63
|
+
# - A Mat5Reader object.
|
64
|
+
#
|
65
|
+
def load_file(file_path)
|
66
|
+
NMatrix::IO::Mat5Reader.new(File.open(file_path, 'rb')).to_ruby
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
54
70
|
# TODO: Make this actually pretty.
|
55
|
-
def pretty_print(q
|
56
|
-
if dim
|
57
|
-
|
71
|
+
def pretty_print(q) #:nodoc:
|
72
|
+
if self.dim > 3 || self.dim == 1
|
73
|
+
self.to_a.pretty_print(q)
|
58
74
|
else
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
rescue ArgumentError
|
66
|
-
nil
|
67
|
-
end
|
68
|
-
ary << (o.nil? ? 'nil' : o)
|
75
|
+
# iterate through the whole matrix and find the longest number
|
76
|
+
longest = Array.new(self.shape[1], 0)
|
77
|
+
self.each_column.with_index do |col, j|
|
78
|
+
col.each do |elem|
|
79
|
+
elem_len = elem.to_s.size
|
80
|
+
longest[j] = elem_len if longest[j] < elem_len
|
69
81
|
end
|
70
|
-
ary.inspect
|
71
82
|
end
|
72
83
|
|
73
|
-
if
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
84
|
+
if self.dim == 3
|
85
|
+
q.group(0, "\n{ layers:", "}") do
|
86
|
+
self.each_layer.with_index do |layer,k|
|
87
|
+
q.group(0, "\n [\n", " ]\n") do
|
88
|
+
layer.each_row.with_index do |row,i|
|
89
|
+
q.group(0, " [", "]\n") do
|
90
|
+
q.seplist(self[i,0...self.shape[1],k].to_flat_array, lambda { q.text ", "}, :each_with_index) { |v,j| q.text v.to_s.rjust(longest[j]) }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
else # dim 2
|
97
|
+
q.group(0, "\n[\n", "]") do
|
98
|
+
self.each_row.with_index do |row,i|
|
99
|
+
q.group(1, " [", "]") do
|
100
|
+
q.seplist(self.dim > 2 ? row.to_a[0] : row.to_a, lambda { q.text ", " }, :each_with_index) { |v,j| q.text v.to_s.rjust(longest[j]) }
|
101
|
+
end
|
102
|
+
q.breakable
|
103
|
+
end
|
78
104
|
end
|
79
105
|
end
|
80
|
-
|
81
106
|
end
|
82
107
|
end
|
83
|
-
alias :pp :pretty_print
|
108
|
+
#alias :pp :pretty_print
|
109
|
+
|
84
110
|
|
85
111
|
|
86
112
|
#
|
@@ -172,514 +198,234 @@ class NMatrix
|
|
172
198
|
end
|
173
199
|
alias :to_h :to_hash
|
174
200
|
|
175
|
-
#
|
176
|
-
# call-seq:
|
177
|
-
# invert! -> NMatrix
|
178
|
-
#
|
179
|
-
# Use LAPACK to calculate the inverse of the matrix (in-place). Only works on
|
180
|
-
# dense matrices.
|
181
|
-
#
|
182
|
-
# Note: If you don't have LAPACK, e.g., on a Mac, this may not work yet.
|
183
|
-
#
|
184
|
-
def invert!
|
185
|
-
# Get the pivot array; factor the matrix
|
186
|
-
pivot = self.getrf!
|
187
201
|
|
188
|
-
|
189
|
-
|
202
|
+
def inspect #:nodoc:
|
203
|
+
original_inspect = super()
|
204
|
+
original_inspect = original_inspect[0...original_inspect.size-1]
|
205
|
+
original_inspect + " " + inspect_helper.join(" ") + ">"
|
206
|
+
end
|
207
|
+
|
208
|
+
def __yale_ary__to_s(sym) #:nodoc:
|
209
|
+
ary = self.send("__yale_#{sym.to_s}__".to_sym)
|
190
210
|
|
191
|
-
|
211
|
+
'[' + ary.collect { |a| a ? a : 'nil'}.join(',') + ']'
|
192
212
|
end
|
193
213
|
|
214
|
+
|
215
|
+
##
|
216
|
+
# call-seq:
|
217
|
+
# integer_dtype?() -> Boolean
|
194
218
|
#
|
195
|
-
#
|
196
|
-
# invert -> NMatrix
|
197
|
-
#
|
198
|
-
# Make a copy of the matrix, then invert it (requires LAPACK).
|
199
|
-
#
|
200
|
-
# * *Returns* :
|
201
|
-
# - A dense NMatrix.
|
219
|
+
# Checks if dtype is an integer type
|
202
220
|
#
|
203
|
-
def
|
204
|
-
|
221
|
+
def integer_dtype?
|
222
|
+
[:byte, :int8, :int16, :int32, :int64].include?(self.dtype)
|
205
223
|
end
|
206
|
-
|
224
|
+
|
207
225
|
|
208
226
|
#
|
209
227
|
# call-seq:
|
210
|
-
#
|
228
|
+
# to_f -> Float
|
211
229
|
#
|
212
|
-
#
|
213
|
-
#
|
230
|
+
# Converts an nmatrix with a single element (but any number of dimensions)
|
231
|
+
# to a float.
|
214
232
|
#
|
215
|
-
#
|
216
|
-
# - The IPIV vector. The L and U matrices are stored in A.
|
217
|
-
# * *Raises* :
|
218
|
-
# - +StorageTypeError+ -> ATLAS functions only work on dense matrices.
|
233
|
+
# Raises an IndexError if the matrix does not have just a single element.
|
219
234
|
#
|
220
|
-
def
|
221
|
-
raise
|
222
|
-
|
235
|
+
def to_f
|
236
|
+
raise IndexError, 'to_f only valid for matrices with a single element' unless shape.all? { |e| e == 1 }
|
237
|
+
self[*Array.new(shape.size, 0)]
|
223
238
|
end
|
224
239
|
|
225
240
|
#
|
226
241
|
# call-seq:
|
227
|
-
#
|
228
|
-
#
|
229
|
-
# Calculate the determinant by way of LU decomposition. This is accomplished
|
230
|
-
# using clapack_getrf, and then by summing the diagonal elements. There is a
|
231
|
-
# risk of underflow/overflow.
|
232
|
-
#
|
233
|
-
# There are probably also more efficient ways to calculate the determinant.
|
234
|
-
# This method requires making a copy of the matrix, since clapack_getrf
|
235
|
-
# modifies its input.
|
242
|
+
# to_flat_array -> Array
|
243
|
+
# to_flat_a -> Array
|
236
244
|
#
|
237
|
-
#
|
245
|
+
# Converts an NMatrix to a one-dimensional Ruby Array.
|
238
246
|
#
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
247
|
+
def to_flat_array
|
248
|
+
ary = Array.new(self.size)
|
249
|
+
self.each.with_index { |v,i| ary[i] = v }
|
250
|
+
ary
|
251
|
+
end
|
252
|
+
alias :to_flat_a :to_flat_array
|
253
|
+
|
243
254
|
#
|
244
|
-
#
|
245
|
-
#
|
255
|
+
# call-seq:
|
256
|
+
# size -> Fixnum
|
246
257
|
#
|
247
|
-
#
|
248
|
-
# - The determinant of the matrix. It's the same type as the matrix's dtype.
|
249
|
-
# * *Raises* :
|
250
|
-
# - +NotImplementedError+ -> Must be used in 2D matrices.
|
258
|
+
# Returns the total size of the NMatrix based on its shape.
|
251
259
|
#
|
252
|
-
def
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
new_dtype = [:byte,:int8,:int16,:int32,:int64].include?(self.dtype) ? :rational128 : self.dtype
|
257
|
-
copy = self.cast(:dense, new_dtype)
|
258
|
-
|
259
|
-
# Need to know the number of permutations. We'll add up the diagonals of
|
260
|
-
# the factorized matrix.
|
261
|
-
pivot = copy.getrf!
|
260
|
+
def size
|
261
|
+
s = self.shape
|
262
|
+
(0...self.dimensions).inject(1) { |x,i| x * s[i] }
|
263
|
+
end
|
262
264
|
|
263
|
-
prod = pivot.size % 2 == 1 ? -1 : 1 # odd permutations => negative
|
264
|
-
[shape[0],shape[1]].min.times do |i|
|
265
|
-
prod *= copy[i,i]
|
266
|
-
end
|
267
265
|
|
268
|
-
|
269
|
-
|
266
|
+
def to_s #:nodoc:
|
267
|
+
self.to_flat_array.to_s
|
270
268
|
end
|
271
269
|
|
272
270
|
#
|
273
271
|
# call-seq:
|
274
|
-
#
|
275
|
-
# complex_conjugate(new_stype) -> NMatrix
|
276
|
-
#
|
277
|
-
# Get the complex conjugate of this matrix. See also complex_conjugate! for
|
278
|
-
# an in-place operation (provided the dtype is already +:complex64+ or
|
279
|
-
# +:complex128+).
|
280
|
-
#
|
281
|
-
# Doesn't work on list matrices, but you can optionally pass in the stype you
|
282
|
-
# want to cast to if you're dealing with a list matrix.
|
272
|
+
# nvector? -> true or false
|
283
273
|
#
|
284
|
-
#
|
285
|
-
#
|
286
|
-
# * *Returns* :
|
287
|
-
# - If the original NMatrix isn't complex, the result is a +:complex128+ NMatrix. Otherwise, it's the original dtype.
|
274
|
+
# Shortcut function for determining whether the effective dimension is less than the dimension.
|
275
|
+
# Useful when we take slices of n-dimensional matrices where n > 2.
|
288
276
|
#
|
289
|
-
def
|
290
|
-
self.
|
277
|
+
def nvector?
|
278
|
+
self.effective_dim < self.dim
|
291
279
|
end
|
292
280
|
|
293
281
|
#
|
294
282
|
# call-seq:
|
295
|
-
#
|
283
|
+
# vector? -> true or false
|
296
284
|
#
|
297
|
-
#
|
298
|
-
# complex, this should only require one copy (for the transpose).
|
299
|
-
#
|
300
|
-
# * *Returns* :
|
301
|
-
# - The conjugate transpose of the matrix as a copy.
|
285
|
+
# Shortcut function for determining whether the effective dimension is 1. See also #nvector?
|
302
286
|
#
|
303
|
-
def
|
304
|
-
self.
|
287
|
+
def vector?
|
288
|
+
self.effective_dim == 1
|
305
289
|
end
|
306
290
|
|
291
|
+
|
307
292
|
#
|
308
293
|
# call-seq:
|
309
|
-
#
|
310
|
-
#
|
311
|
-
# A hermitian matrix is a complex square matrix that is equal to its
|
312
|
-
# conjugate transpose. (http://en.wikipedia.org/wiki/Hermitian_matrix)
|
294
|
+
# to_a -> Array
|
313
295
|
#
|
314
|
-
#
|
315
|
-
# - True if +self+ is a hermitian matrix, nil otherwise.
|
296
|
+
# Converts an NMatrix to an array of arrays, or an NMatrix of effective dimension 1 to an array.
|
316
297
|
#
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
if [:complex64, :complex128].include?(self.dtype)
|
321
|
-
# TODO: Write much faster Hermitian test in C
|
322
|
-
self.eql?(self.conjugate_transpose)
|
323
|
-
else
|
324
|
-
symmetric?
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
def inspect #:nodoc:
|
329
|
-
original_inspect = super()
|
330
|
-
original_inspect = original_inspect[0...original_inspect.size-1]
|
331
|
-
original_inspect + " " + inspect_helper.join(" ") + ">"
|
332
|
-
end
|
333
|
-
|
334
|
-
def __yale_ary__to_s(sym) #:nodoc:
|
335
|
-
ary = self.send("__yale_#{sym.to_s}__".to_sym)
|
336
|
-
|
337
|
-
'[' + ary.collect { |a| a ? a : 'nil'}.join(',') + ']'
|
338
|
-
end
|
298
|
+
# Does not yet work for dimensions > 2
|
299
|
+
def to_a(dimen=nil)
|
300
|
+
if self.dim == 2
|
339
301
|
|
302
|
+
return self.to_flat_a if self.shape[0] == 1
|
340
303
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
# each_along_dim() { |elem| block } -> NMatrix
|
346
|
-
# each_along_dim(dimen) { |elem| block } -> NMatrix
|
347
|
-
#
|
348
|
-
# Successively yields submatrices at each coordinate along a specified
|
349
|
-
# dimension. Each submatrix will have the same number of dimensions as
|
350
|
-
# the matrix being iterated, but with the specified dimension's size
|
351
|
-
# equal to 1.
|
352
|
-
#
|
353
|
-
# @param [Integer] dimen the dimension being iterated over.
|
354
|
-
#
|
355
|
-
def each_along_dim(dimen=0)
|
356
|
-
return enum_for(:each_along_dim, dimen) unless block_given?
|
357
|
-
dims = shape
|
358
|
-
shape.each_index { |i| dims[i] = 0...(shape[i]) unless i == dimen }
|
359
|
-
0.upto(shape[dimen]-1) do |i|
|
360
|
-
dims[dimen] = i
|
361
|
-
yield self[*dims]
|
362
|
-
end
|
363
|
-
end
|
364
|
-
|
365
|
-
|
366
|
-
##
|
367
|
-
# call-seq:
|
368
|
-
# reduce_along_dim() -> Enumerator
|
369
|
-
# reduce_along_dim(dimen) -> Enumerator
|
370
|
-
# reduce_along_dim(dimen, initial) -> Enumerator
|
371
|
-
# reduce_along_dim(dimen, initial, dtype) -> Enumerator
|
372
|
-
# reduce_along_dim() { |elem| block } -> NMatrix
|
373
|
-
# reduce_along_dim(dimen) { |elem| block } -> NMatrix
|
374
|
-
# reduce_along_dim(dimen, initial) { |elem| block } -> NMatrix
|
375
|
-
# reduce_along_dim(dimen, initial, dtype) { |elem| block } -> NMatrix
|
376
|
-
#
|
377
|
-
# Reduces an NMatrix using a supplied block over a specified dimension.
|
378
|
-
# The block should behave the same way as for Enumerable#reduce.
|
379
|
-
#
|
380
|
-
# @param [Integer] dimen the dimension being reduced
|
381
|
-
# @param [Numeric] initial the initial value for the reduction
|
382
|
-
# (i.e. the usual parameter to Enumerable#reduce). Supply nil or do not
|
383
|
-
# supply this argument to have it follow the usual Enumerable#reduce
|
384
|
-
# behavior of using the first element as the initial value.
|
385
|
-
# @param [Symbol] dtype if non-nil/false, forces the accumulated result to have this dtype
|
386
|
-
# @return [NMatrix] an NMatrix with the same number of dimensions as the
|
387
|
-
# input, but with the input dimension now having size 1. Each element
|
388
|
-
# is the result of the reduction at that position along the specified
|
389
|
-
# dimension.
|
390
|
-
#
|
391
|
-
def reduce_along_dim(dimen=0, initial=nil, dtype=nil)
|
392
|
-
|
393
|
-
if dimen > shape.size then
|
394
|
-
raise ArgumentError, "Requested dimension does not exist. Requested: #{dimen}, shape: #{shape}"
|
395
|
-
end
|
396
|
-
|
397
|
-
return enum_for(:reduce_along_dim, dimen, initial) unless block_given?
|
398
|
-
|
399
|
-
new_shape = shape
|
400
|
-
new_shape[dimen] = 1
|
401
|
-
|
402
|
-
first_as_acc = false
|
403
|
-
|
404
|
-
if initial then
|
405
|
-
acc = NMatrix.new(new_shape, initial, dtype || self.dtype)
|
406
|
-
else
|
407
|
-
each_along_dim(dimen) do |sub_mat|
|
408
|
-
if sub_mat.is_a?(NMatrix) and dtype and dtype != self.dtype then
|
409
|
-
acc = sub_mat.cast(self.stype, dtype)
|
410
|
-
else
|
411
|
-
acc = sub_mat
|
304
|
+
ary = []
|
305
|
+
begin
|
306
|
+
self.each_row do |row|
|
307
|
+
ary << row.to_flat_a
|
412
308
|
end
|
413
|
-
|
309
|
+
#rescue NotImplementedError # Oops. Try copying instead
|
310
|
+
# self.each_row(:copy) do |row|
|
311
|
+
# ary << row.to_a.flatten
|
312
|
+
# end
|
414
313
|
end
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
each_along_dim(dimen) do |sub_mat|
|
419
|
-
if first_as_acc then
|
420
|
-
first_as_acc = false
|
421
|
-
next
|
422
|
-
end
|
423
|
-
acc = (yield acc, sub_mat)
|
314
|
+
ary
|
315
|
+
else
|
316
|
+
to_a_rec(0)
|
424
317
|
end
|
425
|
-
|
426
|
-
acc
|
427
|
-
|
428
318
|
end
|
429
319
|
|
430
|
-
alias_method :inject_along_dim, :reduce_along_dim
|
431
320
|
|
432
|
-
##
|
433
|
-
# call-seq:
|
434
|
-
# integer_dtype?() -> Boolean
|
435
321
|
#
|
436
|
-
# Checks if dtype is an integer type
|
437
|
-
#
|
438
|
-
def integer_dtype?
|
439
|
-
[:byte, :int8, :int16, :int32, :int64].include?(self.dtype)
|
440
|
-
end
|
441
|
-
|
442
|
-
##
|
443
322
|
# call-seq:
|
444
|
-
#
|
445
|
-
#
|
323
|
+
# rank(dimension, row_or_column_number) -> NMatrix
|
324
|
+
# rank(dimension, row_or_column_number, :reference) -> NMatrix reference slice
|
446
325
|
#
|
447
|
-
#
|
326
|
+
# Returns the rank (e.g., row, column, or layer) specified, using slicing by copy as default.
|
448
327
|
#
|
449
|
-
#
|
450
|
-
|
451
|
-
# @see #reduce_along_dim
|
452
|
-
#
|
453
|
-
def mean(dimen=0)
|
454
|
-
reduce_dtype = nil
|
455
|
-
if integer_dtype? then
|
456
|
-
reduce_dtype = :float64
|
457
|
-
end
|
458
|
-
reduce_along_dim(dimen, 0.0, reduce_dtype) do |mean, sub_mat|
|
459
|
-
mean + sub_mat/shape[dimen]
|
460
|
-
end
|
461
|
-
end
|
328
|
+
# See @row (dimension = 0), @column (dimension = 1)
|
329
|
+
def rank(shape_idx, rank_idx, meth = :copy)
|
462
330
|
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
# sum(dimen) -> NMatrix
|
467
|
-
#
|
468
|
-
# Calculates the sum along the specified dimension.
|
469
|
-
#
|
470
|
-
# @see #reduce_along_dim
|
471
|
-
def sum(dimen=0)
|
472
|
-
reduce_along_dim(dimen, 0.0) do |sum, sub_mat|
|
473
|
-
sum + sub_mat
|
331
|
+
params = Array.new(self.dim)
|
332
|
+
params.each.with_index do |v,d|
|
333
|
+
params[d] = d == shape_idx ? rank_idx : 0...self.shape[d]
|
474
334
|
end
|
475
|
-
end
|
476
335
|
|
477
|
-
|
478
|
-
##
|
479
|
-
# call-seq:
|
480
|
-
# min() -> NMatrix
|
481
|
-
# min(dimen) -> NMatrix
|
482
|
-
#
|
483
|
-
# Calculates the minimum along the specified dimension.
|
484
|
-
#
|
485
|
-
# @see #reduce_along_dim
|
486
|
-
#
|
487
|
-
def min(dimen=0)
|
488
|
-
reduce_along_dim(dimen) do |min, sub_mat|
|
489
|
-
if min.is_a? NMatrix then
|
490
|
-
min * (min <= sub_mat).cast(self.stype, self.dtype) + ((min)*0.0 + (min > sub_mat).cast(self.stype, self.dtype)) * sub_mat
|
491
|
-
else
|
492
|
-
min <= sub_mat ? min : sub_mat
|
493
|
-
end
|
494
|
-
end
|
336
|
+
meth == :reference ? self[*params] : self.slice(*params)
|
495
337
|
end
|
496
338
|
|
497
|
-
##
|
498
|
-
# call-seq:
|
499
|
-
# max() -> NMatrix
|
500
|
-
# max(dimen) -> NMatrix
|
501
339
|
#
|
502
|
-
# Calculates the maximum along the specified dimension.
|
503
|
-
#
|
504
|
-
# @see #reduce_along_dim
|
505
|
-
#
|
506
|
-
def max(dimen=0)
|
507
|
-
reduce_along_dim(dimen) do |max, sub_mat|
|
508
|
-
if max.is_a? NMatrix then
|
509
|
-
max * (max >= sub_mat).cast(self.stype, self.dtype) + ((max)*0.0 + (max < sub_mat).cast(self.stype, self.dtype)) * sub_mat
|
510
|
-
else
|
511
|
-
max >= sub_mat ? max : sub_mat
|
512
|
-
end
|
513
|
-
end
|
514
|
-
end
|
515
|
-
|
516
|
-
|
517
|
-
##
|
518
340
|
# call-seq:
|
519
|
-
#
|
520
|
-
#
|
521
|
-
#
|
522
|
-
# Calculates the sample variance along the specified dimension.
|
523
|
-
#
|
524
|
-
# This will force integer types to float64 dtype.
|
525
|
-
#
|
526
|
-
# @see #reduce_along_dim
|
341
|
+
# column(column_number) -> NMatrix
|
342
|
+
# column(column_number, get_by) -> NMatrix
|
527
343
|
#
|
528
|
-
|
529
|
-
reduce_dtype = nil
|
530
|
-
if integer_dtype? then
|
531
|
-
reduce_dtype = :float64
|
532
|
-
end
|
533
|
-
m = mean(dimen)
|
534
|
-
reduce_along_dim(dimen, 0.0, reduce_dtype) do |var, sub_mat|
|
535
|
-
var + (m - sub_mat)*(m - sub_mat)/(shape[dimen]-1)
|
536
|
-
end
|
537
|
-
end
|
538
|
-
|
539
|
-
##
|
540
|
-
# call-seq:
|
541
|
-
# std() -> NMatrix
|
542
|
-
# std(dimen) -> NMatrix
|
344
|
+
# Returns the column specified. Uses slicing by copy as default.
|
543
345
|
#
|
346
|
+
# * *Arguments* :
|
347
|
+
# - +column_number+ -> Integer.
|
348
|
+
# - +get_by+ -> Type of slicing to use, +:copy+ or +:reference+.
|
349
|
+
# * *Returns* :
|
350
|
+
# - A NMatrix representing the requested column as a column vector.
|
544
351
|
#
|
545
|
-
#
|
352
|
+
# Examples:
|
546
353
|
#
|
547
|
-
#
|
354
|
+
# m = NMatrix.new(2, [1, 4, 9, 14], :int32) # => 1 4
|
355
|
+
# 9 14
|
548
356
|
#
|
549
|
-
#
|
357
|
+
# m.column(1) # => 4
|
358
|
+
# 14
|
550
359
|
#
|
551
|
-
def
|
552
|
-
|
360
|
+
def column(column_number, get_by = :copy)
|
361
|
+
rank(1, column_number, get_by)
|
553
362
|
end
|
554
363
|
|
555
|
-
|
556
|
-
# call-seq:
|
557
|
-
# to_f -> Float
|
558
|
-
#
|
559
|
-
# Converts an nmatrix with a single element (but any number of dimensions)
|
560
|
-
# to a float.
|
561
|
-
#
|
562
|
-
# Raises an IndexError if the matrix does not have just a single element.
|
563
|
-
#
|
564
|
-
# FIXME: Does this actually happen? Matrices should not have just one element.
|
565
|
-
def to_f
|
566
|
-
raise IndexError, 'to_f only valid for matrices with a single element' unless shape.all? { |e| e == 1 }
|
567
|
-
self[*Array.new(shape.size, 0)]
|
568
|
-
end
|
364
|
+
alias :col :column
|
569
365
|
|
570
|
-
##
|
571
|
-
# call-seq:
|
572
|
-
# each -> Enumerator
|
573
366
|
#
|
574
|
-
# Enumerate through the matrix. @see Enumerable#each
|
575
|
-
#
|
576
|
-
# For dense, this actually calls a specialized each iterator (in C). For yale and list, it relies upon
|
577
|
-
# #each_with_indices (which is about as fast as reasonably possible for C code).
|
578
|
-
def each &block
|
579
|
-
if self.stype == :dense
|
580
|
-
self.__dense_each__(&block)
|
581
|
-
else
|
582
|
-
self.each_with_indices(&block)
|
583
|
-
end
|
584
|
-
end
|
585
|
-
|
586
|
-
##
|
587
367
|
# call-seq:
|
588
|
-
#
|
589
|
-
#
|
368
|
+
# row(row_number) -> NMatrix
|
369
|
+
# row(row_number, get_by) -> NMatrix
|
590
370
|
#
|
591
|
-
#
|
371
|
+
# * *Arguments* :
|
372
|
+
# - +row_number+ -> Integer.
|
373
|
+
# - +get_by+ -> Type of slicing to use, +:copy+ or +:reference+.
|
374
|
+
# * *Returns* :
|
375
|
+
# - A NMatrix representing the requested row as a row vector.
|
592
376
|
#
|
593
|
-
def
|
594
|
-
|
595
|
-
cp = self.dup
|
596
|
-
cp.map! &bl
|
597
|
-
cp
|
377
|
+
def row(row_number, get_by = :copy)
|
378
|
+
rank(0, row_number, get_by)
|
598
379
|
end
|
599
380
|
|
600
|
-
|
381
|
+
#
|
601
382
|
# call-seq:
|
602
|
-
#
|
603
|
-
#
|
383
|
+
# layer(layer_number) -> NMatrix
|
384
|
+
# row(layer_number, get_by) -> NMatrix
|
604
385
|
#
|
605
|
-
#
|
606
|
-
#
|
386
|
+
# * *Arguments* :
|
387
|
+
# - +layer_number+ -> Integer.
|
388
|
+
# - +get_by+ -> Type of slicing to use, +:copy+ or +:reference+.
|
389
|
+
# * *Returns* :
|
390
|
+
# - A NMatrix representing the requested layer as a layer vector.
|
607
391
|
#
|
608
|
-
def
|
609
|
-
|
610
|
-
self.each_stored_with_indices do |e, *i|
|
611
|
-
self[*i] = (yield e)
|
612
|
-
end
|
613
|
-
self
|
392
|
+
def layer(layer_number, get_by = :copy)
|
393
|
+
rank(2, layer_number, get_by)
|
614
394
|
end
|
615
395
|
|
616
396
|
|
397
|
+
|
617
398
|
#
|
618
399
|
# call-seq:
|
619
|
-
#
|
620
|
-
#
|
621
|
-
#
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
400
|
+
# shuffle! -> ...
|
401
|
+
# shuffle!(random: rng) -> ...
|
402
|
+
#
|
403
|
+
# Re-arranges the contents of an NVector.
|
404
|
+
#
|
405
|
+
# TODO: Write more efficient version for Yale, list.
|
406
|
+
# TODO: Generalize for more dimensions.
|
407
|
+
def shuffle!(*args)
|
408
|
+
method_missing(:shuffle!, *args) if self.effective_dim > 1
|
409
|
+
ary = self.to_flat_a
|
410
|
+
ary.shuffle!(*args)
|
411
|
+
ary.each.with_index { |v,idx| self[idx] = v }
|
626
412
|
self
|
627
413
|
end
|
628
414
|
|
415
|
+
|
629
416
|
#
|
630
417
|
# call-seq:
|
631
|
-
#
|
418
|
+
# shuffle -> ...
|
419
|
+
# shuffle(rng) -> ...
|
632
420
|
#
|
633
|
-
#
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
self
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
class << self
|
643
|
-
#
|
644
|
-
# call-seq:
|
645
|
-
# load_file(path) -> Mat5Reader
|
646
|
-
#
|
647
|
-
# * *Arguments* :
|
648
|
-
# - +path+ -> The path to a version 5 .mat file.
|
649
|
-
# * *Returns* :
|
650
|
-
# - A Mat5Reader object.
|
651
|
-
#
|
652
|
-
def load_file(file_path)
|
653
|
-
NMatrix::IO::Mat5Reader.new(File.open(file_path, 'rb')).to_ruby
|
654
|
-
end
|
655
|
-
|
656
|
-
##
|
657
|
-
# call-seq:
|
658
|
-
# ones_like(nm) -> NMatrix
|
659
|
-
#
|
660
|
-
# Creates a new matrix of ones with the same dtype and shape as the
|
661
|
-
# provided matrix.
|
662
|
-
#
|
663
|
-
# @param [NMatrix] nm the nmatrix whose dtype and shape will be used
|
664
|
-
# @return [NMatrix] a new nmatrix filled with ones.
|
665
|
-
#
|
666
|
-
def ones_like(nm)
|
667
|
-
NMatrix.ones(nm.shape, nm.dtype)
|
668
|
-
end
|
669
|
-
|
670
|
-
##
|
671
|
-
# call-seq:
|
672
|
-
# zeros_like(nm) -> NMatrix
|
673
|
-
#
|
674
|
-
# Creates a new matrix of zeros with the same stype, dtype, and shape
|
675
|
-
# as the provided matrix.
|
676
|
-
#
|
677
|
-
# @param [NMatrix] nm the nmatrix whose stype, dtype, and shape will be used
|
678
|
-
# @return [NMatrix] a new nmatrix filled with zeros.
|
679
|
-
#
|
680
|
-
def zeros_like(nm)
|
681
|
-
NMatrix.zeros(nm.stype, nm.shape, nm.dtype)
|
682
|
-
end
|
421
|
+
# Re-arranges the contents of an NVector.
|
422
|
+
#
|
423
|
+
# TODO: Write more efficient version for Yale, list.
|
424
|
+
# TODO: Generalize for more dimensions.
|
425
|
+
def shuffle(*args)
|
426
|
+
method_missing(:shuffle!, *args) if self.effective_dim > 1
|
427
|
+
t = self.clone
|
428
|
+
t.shuffle!(*args)
|
683
429
|
end
|
684
430
|
|
685
431
|
|
@@ -693,54 +439,18 @@ class NMatrix
|
|
693
439
|
end
|
694
440
|
end
|
695
441
|
|
696
|
-
protected
|
697
|
-
# Define the element-wise operations for lists. Note that the __list_map_merged_stored__ iterator returns a Ruby Object
|
698
|
-
# matrix, which we then cast back to the appropriate type. If you don't want that, you can redefine these functions in
|
699
|
-
# your own code.
|
700
|
-
{add: :+, sub: :-, mul: :*, div: :/, pow: :**, mod: :%}.each_pair do |ewop, op|
|
701
|
-
define_method("__list_elementwise_#{ewop}__") do |rhs|
|
702
|
-
self.__list_map_merged_stored__(rhs, nil) { |l,r| l.send(op,r) }.cast(stype, NMatrix.upcast(dtype, rhs.dtype))
|
703
|
-
end
|
704
|
-
define_method("__dense_elementwise_#{ewop}__") do |rhs|
|
705
|
-
self.__dense_map_pair__(rhs) { |l,r| l.send(op,r) }.cast(stype, NMatrix.upcast(dtype, rhs.dtype))
|
706
|
-
end
|
707
|
-
define_method("__yale_elementwise_#{ewop}__") do |rhs|
|
708
|
-
self.__yale_map_merged_stored__(rhs, nil) { |l,r| l.send(op,r) }.cast(stype, NMatrix.upcast(dtype, rhs.dtype))
|
709
|
-
end
|
710
|
-
define_method("__list_scalar_#{ewop}__") do |rhs|
|
711
|
-
self.__list_map_merged_stored__(rhs, nil) { |l,r| l.send(op,r) }.cast(stype, NMatrix.upcast(dtype, NMatrix.min_dtype(rhs)))
|
712
|
-
end
|
713
|
-
define_method("__yale_scalar_#{ewop}__") do |rhs|
|
714
|
-
self.__yale_map_stored__ { |l| l.send(op,rhs) }.cast(stype, NMatrix.upcast(dtype, NMatrix.min_dtype(rhs)))
|
715
|
-
end
|
716
|
-
define_method("__dense_scalar_#{ewop}__") do |rhs|
|
717
|
-
self.__dense_map__ { |l| l.send(op,rhs) }.cast(stype, NMatrix.upcast(dtype, NMatrix.min_dtype(rhs)))
|
718
|
-
end
|
719
|
-
end
|
720
442
|
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
end
|
729
|
-
define_method("__yale_elementwise_#{ewop}__") do |rhs|
|
730
|
-
self.__yale_map_merged_stored__(rhs, nil) { |l,r| l.send(op,r) }
|
731
|
-
end
|
732
|
-
|
733
|
-
define_method("__list_scalar_#{ewop}__") do |rhs|
|
734
|
-
self.__list_map_merged_stored__(rhs, nil) { |l,r| l.send(op,r) }
|
735
|
-
end
|
736
|
-
define_method("__yale_scalar_#{ewop}__") do |rhs|
|
737
|
-
self.__yale_map_stored__ { |l| l.send(op,rhs) }
|
738
|
-
end
|
739
|
-
define_method("__dense_scalar_#{ewop}__") do |rhs|
|
740
|
-
self.__dense_map__ { |l| l.send(op,rhs) }
|
443
|
+
def respond_to?(method) #:nodoc:
|
444
|
+
if [:shuffle, :shuffle!, :each_with_index].include?(method.intern) # vector-only methods
|
445
|
+
return vector?
|
446
|
+
elsif [:each_layer, :layer].include?(method.intern) # 3-or-more dimensions only
|
447
|
+
return dim > 2
|
448
|
+
else
|
449
|
+
super(method)
|
741
450
|
end
|
742
451
|
end
|
743
452
|
|
453
|
+
|
744
454
|
# This is how you write an individual element-wise operation function:
|
745
455
|
#def __list_elementwise_add__ rhs
|
746
456
|
# self.__list_map_merged_stored__(rhs){ |l,r| l+r }.cast(self.stype, NMatrix.upcast(self.dtype, rhs.dtype))
|
@@ -766,4 +476,18 @@ protected
|
|
766
476
|
end
|
767
477
|
|
768
478
|
|
479
|
+
# Helper for converting a matrix into an array of arrays recursively
|
480
|
+
def to_a_rec(dimen = 0) #:nodoc:
|
481
|
+
return self.flat_map { |v| v } if dimen == self.dim-1
|
482
|
+
|
483
|
+
ary = []
|
484
|
+
self.each_rank(dimen) do |sect|
|
485
|
+
ary << sect.to_a_rec(dimen+1)
|
486
|
+
end
|
487
|
+
ary
|
488
|
+
end
|
769
489
|
end
|
490
|
+
|
491
|
+
require_relative './shortcuts.rb'
|
492
|
+
require_relative './math.rb'
|
493
|
+
require_relative './enumerate.rb'
|