pnmatrix 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/ext/nmatrix/binary_format.txt +53 -0
  3. data/ext/nmatrix/data/complex.h +388 -0
  4. data/ext/nmatrix/data/data.cpp +274 -0
  5. data/ext/nmatrix/data/data.h +651 -0
  6. data/ext/nmatrix/data/meta.h +64 -0
  7. data/ext/nmatrix/data/ruby_object.h +386 -0
  8. data/ext/nmatrix/extconf.rb +70 -0
  9. data/ext/nmatrix/math/asum.h +99 -0
  10. data/ext/nmatrix/math/cblas_enums.h +36 -0
  11. data/ext/nmatrix/math/cblas_templates_core.h +507 -0
  12. data/ext/nmatrix/math/gemm.h +241 -0
  13. data/ext/nmatrix/math/gemv.h +178 -0
  14. data/ext/nmatrix/math/getrf.h +255 -0
  15. data/ext/nmatrix/math/getrs.h +121 -0
  16. data/ext/nmatrix/math/imax.h +82 -0
  17. data/ext/nmatrix/math/laswp.h +165 -0
  18. data/ext/nmatrix/math/long_dtype.h +62 -0
  19. data/ext/nmatrix/math/magnitude.h +54 -0
  20. data/ext/nmatrix/math/math.h +751 -0
  21. data/ext/nmatrix/math/nrm2.h +165 -0
  22. data/ext/nmatrix/math/rot.h +117 -0
  23. data/ext/nmatrix/math/rotg.h +106 -0
  24. data/ext/nmatrix/math/scal.h +71 -0
  25. data/ext/nmatrix/math/trsm.h +336 -0
  26. data/ext/nmatrix/math/util.h +162 -0
  27. data/ext/nmatrix/math.cpp +1368 -0
  28. data/ext/nmatrix/nm_memory.h +60 -0
  29. data/ext/nmatrix/nmatrix.cpp +285 -0
  30. data/ext/nmatrix/nmatrix.h +476 -0
  31. data/ext/nmatrix/ruby_constants.cpp +151 -0
  32. data/ext/nmatrix/ruby_constants.h +106 -0
  33. data/ext/nmatrix/ruby_nmatrix.c +3130 -0
  34. data/ext/nmatrix/storage/common.cpp +77 -0
  35. data/ext/nmatrix/storage/common.h +183 -0
  36. data/ext/nmatrix/storage/dense/dense.cpp +1096 -0
  37. data/ext/nmatrix/storage/dense/dense.h +129 -0
  38. data/ext/nmatrix/storage/list/list.cpp +1628 -0
  39. data/ext/nmatrix/storage/list/list.h +138 -0
  40. data/ext/nmatrix/storage/storage.cpp +730 -0
  41. data/ext/nmatrix/storage/storage.h +99 -0
  42. data/ext/nmatrix/storage/yale/class.h +1139 -0
  43. data/ext/nmatrix/storage/yale/iterators/base.h +143 -0
  44. data/ext/nmatrix/storage/yale/iterators/iterator.h +131 -0
  45. data/ext/nmatrix/storage/yale/iterators/row.h +450 -0
  46. data/ext/nmatrix/storage/yale/iterators/row_stored.h +140 -0
  47. data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +169 -0
  48. data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +124 -0
  49. data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
  50. data/ext/nmatrix/storage/yale/yale.cpp +2074 -0
  51. data/ext/nmatrix/storage/yale/yale.h +203 -0
  52. data/ext/nmatrix/types.h +55 -0
  53. data/ext/nmatrix/util/io.cpp +279 -0
  54. data/ext/nmatrix/util/io.h +115 -0
  55. data/ext/nmatrix/util/sl_list.cpp +627 -0
  56. data/ext/nmatrix/util/sl_list.h +144 -0
  57. data/ext/nmatrix/util/util.h +78 -0
  58. data/lib/nmatrix/blas.rb +378 -0
  59. data/lib/nmatrix/cruby/math.rb +744 -0
  60. data/lib/nmatrix/enumerate.rb +253 -0
  61. data/lib/nmatrix/homogeneous.rb +241 -0
  62. data/lib/nmatrix/io/fortran_format.rb +138 -0
  63. data/lib/nmatrix/io/harwell_boeing.rb +221 -0
  64. data/lib/nmatrix/io/market.rb +263 -0
  65. data/lib/nmatrix/io/point_cloud.rb +189 -0
  66. data/lib/nmatrix/jruby/decomposition.rb +24 -0
  67. data/lib/nmatrix/jruby/enumerable.rb +13 -0
  68. data/lib/nmatrix/jruby/error.rb +4 -0
  69. data/lib/nmatrix/jruby/math.rb +501 -0
  70. data/lib/nmatrix/jruby/nmatrix_java.rb +840 -0
  71. data/lib/nmatrix/jruby/operators.rb +283 -0
  72. data/lib/nmatrix/jruby/slice.rb +264 -0
  73. data/lib/nmatrix/lapack_core.rb +181 -0
  74. data/lib/nmatrix/lapack_plugin.rb +44 -0
  75. data/lib/nmatrix/math.rb +953 -0
  76. data/lib/nmatrix/mkmf.rb +100 -0
  77. data/lib/nmatrix/monkeys.rb +137 -0
  78. data/lib/nmatrix/nmatrix.rb +1172 -0
  79. data/lib/nmatrix/rspec.rb +75 -0
  80. data/lib/nmatrix/shortcuts.rb +1163 -0
  81. data/lib/nmatrix/version.rb +39 -0
  82. data/lib/nmatrix/yale_functions.rb +118 -0
  83. data/lib/nmatrix.rb +28 -0
  84. data/spec/00_nmatrix_spec.rb +892 -0
  85. data/spec/01_enum_spec.rb +196 -0
  86. data/spec/02_slice_spec.rb +407 -0
  87. data/spec/03_nmatrix_monkeys_spec.rb +80 -0
  88. data/spec/2x2_dense_double.mat +0 -0
  89. data/spec/4x4_sparse.mat +0 -0
  90. data/spec/4x5_dense.mat +0 -0
  91. data/spec/blas_spec.rb +215 -0
  92. data/spec/elementwise_spec.rb +311 -0
  93. data/spec/homogeneous_spec.rb +100 -0
  94. data/spec/io/fortran_format_spec.rb +88 -0
  95. data/spec/io/harwell_boeing_spec.rb +98 -0
  96. data/spec/io/test.rua +9 -0
  97. data/spec/io_spec.rb +159 -0
  98. data/spec/lapack_core_spec.rb +482 -0
  99. data/spec/leakcheck.rb +16 -0
  100. data/spec/math_spec.rb +1363 -0
  101. data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
  102. data/spec/nmatrix_yale_spec.rb +286 -0
  103. data/spec/rspec_monkeys.rb +56 -0
  104. data/spec/rspec_spec.rb +35 -0
  105. data/spec/shortcuts_spec.rb +474 -0
  106. data/spec/slice_set_spec.rb +162 -0
  107. data/spec/spec_helper.rb +172 -0
  108. data/spec/stat_spec.rb +214 -0
  109. data/spec/test.pcd +20 -0
  110. data/spec/utm5940.mtx +83844 -0
  111. metadata +295 -0
@@ -0,0 +1,1172 @@
1
+ #--
2
+ # = NMatrix
3
+ #
4
+ # A linear algebra library for scientific computation in Ruby.
5
+ # NMatrix is part of SciRuby.
6
+ #
7
+ # NMatrix was originally inspired by and derived from NArray, by
8
+ # Masahiro Tanaka: http://narray.rubyforge.org
9
+ #
10
+ # == Copyright Information
11
+ #
12
+ # SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
13
+ # NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
14
+ #
15
+ # Please see LICENSE.txt for additional copyright notices.
16
+ #
17
+ # == Contributing
18
+ #
19
+ # By contributing source code to SciRuby, you agree to be bound by
20
+ # our Contributor Agreement:
21
+ #
22
+ # * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
23
+ #
24
+ # == nmatrix.rb
25
+ #
26
+ # This file loads the C extension for NMatrix and all the ruby
27
+ # files and contains those core functionalities which can be
28
+ # implemented efficiently (or much more easily) in Ruby (e.g.,
29
+ # inspect, pretty_print, element-wise operations).
30
+ #++
31
+
32
+ # For some reason nmatrix.so ends up in a different place during gem build.
33
+
34
+ # Detect java
35
+ def jruby?
36
+ /java/ === RUBY_PLATFORM
37
+ end
38
+
39
+ if jruby?
40
+ require_relative 'jruby/nmatrix_java'
41
+ else
42
+ if File.exist?("lib/nmatrix/nmatrix.so") #|| File.exist?("lib/nmatrix/nmatrix.bundle")
43
+ # Development
44
+ require_relative "nmatrix/nmatrix.so"
45
+ else
46
+ # Gem
47
+ require_relative "../nmatrix.so"
48
+ require_relative './io/market'
49
+ require_relative './io/point_cloud'
50
+
51
+ require_relative './lapack_core.rb'
52
+ require_relative './yale_functions.rb'
53
+ end
54
+ end
55
+
56
+ require_relative './math.rb'
57
+ require_relative './monkeys'
58
+
59
+ # NMatrix is a matrix class that supports both multidimensional arrays
60
+ # (`:dense` stype) and sparse storage (`:list` or `:yale` stypes) and 13 data
61
+ # types, including complex numbers, various integer and
62
+ # floating-point sizes and ruby objects.
63
+ class NMatrix
64
+ # Read and write extensions for NMatrix.
65
+ module IO
66
+ extend AutoloadPatch
67
+
68
+ # Reader (and eventually writer) of Matlab .mat files.
69
+ #
70
+ # The .mat file format is documented in the following link:
71
+ # * http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf
72
+ module Matlab
73
+ extend AutoloadPatch
74
+
75
+ class << self
76
+ # call-seq:
77
+ # load(mat_file_path) -> NMatrix
78
+ # load_mat(mat_file_path) -> NMatrix
79
+ #
80
+ # Load a .mat file and return a NMatrix corresponding to it.
81
+ def load_mat(file_path)
82
+ NMatrix::IO::Matlab::Mat5Reader.new(File.open(file_path, "rb+")).to_ruby
83
+ end
84
+ alias :load :load_mat
85
+ end
86
+ end
87
+ end
88
+
89
+ class << self
90
+ # call-seq:
91
+ # load_matlab_file(path) -> Mat5Reader
92
+ #
93
+ # * *Arguments* :
94
+ # - +file_path+ -> The path to a version 5 .mat file.
95
+ # * *Returns* :
96
+ # - A Mat5Reader object.
97
+ def load_matlab_file(file_path)
98
+ NMatrix::IO::Matlab::Mat5Reader.new(File.open(file_path, 'rb')).to_ruby
99
+ end
100
+
101
+ # call-seq:
102
+ # load_pcd_file(path) -> PointCloudReader::MetaReader
103
+ #
104
+ # * *Arguments* :
105
+ # - +file_path+ -> The path to a PCL PCD file.
106
+ # * *Returns* :
107
+ # - A PointCloudReader::MetaReader object with the matrix stored in its +matrix+ property
108
+ def load_pcd_file(file_path)
109
+ NMatrix::IO::PointCloudReader::MetaReader.new(file_path)
110
+ end
111
+
112
+ # Calculate the size of an NMatrix of a given shape.
113
+ def size(shape)
114
+ shape = [shape,shape] unless shape.is_a?(Array)
115
+ (0...shape.size).inject(1) { |x,i| x * shape[i] }
116
+ end
117
+
118
+ # Make N-D coordinate arrays for vectorized evaluations of
119
+ # N-D scalar/vector fields over N-D grids, given N
120
+ # coordinate arrays arrs. N > 1.
121
+ #
122
+ # call-seq:
123
+ # meshgrid(arrs) -> Array of NMatrix
124
+ # meshgrid(arrs, options) -> Array of NMatrix
125
+ #
126
+ # * *Arguments* :
127
+ # - +vectors+ -> Array of N coordinate arrays (Array or NMatrix), if any have more than one dimension they will be flatten
128
+ # - +options+ -> Hash with options (:sparse Boolean, false by default; :indexing Symbol, may be :ij or :xy, :xy by default)
129
+ # * *Returns* :
130
+ # - Array of N N-D NMatrixes
131
+ # * *Examples* :
132
+ # x, y = NMatrix::meshgrid([[1, [2, 3]], [4, 5]])
133
+ # x.to_a #<= [[1, 2, 3], [1, 2, 3]]
134
+ # y.to_a #<= [[4, 4, 4], [5, 5, 5]]
135
+ #
136
+ # * *Using* *options* :
137
+ #
138
+ # x, y = NMatrix::meshgrid([[[1, 2], 3], [4, 5]], sparse: true)
139
+ # x.to_a #<= [[1, 2, 3]]
140
+ # y.to_a #<= [[4], [5]]
141
+ #
142
+ # x, y = NMatrix::meshgrid([[1, 2, 3], [[4], 5]], indexing: :ij)
143
+ # x.to_a #<= [[1, 1], [2, 2], [3, 3]]
144
+ # y.to_a #<= [[4, 5], [4, 5], [4, 5]]
145
+ def meshgrid(vectors, options = {})
146
+ raise(ArgumentError, 'Expected at least 2 arrays.') if vectors.size < 2
147
+ options[:indexing] ||= :xy
148
+ raise(ArgumentError, 'Indexing must be :xy of :ij') unless [:ij, :xy].include? options[:indexing]
149
+ mats = vectors.map { |arr| arr.respond_to?(:flatten) ? arr.flatten : arr.to_flat_array }
150
+ mats[0], mats[1] = mats[1], mats[0] if options[:indexing] == :xy
151
+ new_dim = mats.size
152
+ lengths = mats.map(&:size)
153
+ result = mats.map.with_index do |matrix, axis|
154
+ if options[:sparse]
155
+ new_shape = Array.new(new_dim, 1)
156
+ new_shape[axis] = lengths[axis]
157
+ new_elements = matrix
158
+ else
159
+ before_axis = lengths[0...axis].reduce(:*)
160
+ after_axis = lengths[(axis+1)..-1].reduce(:*)
161
+ new_shape = lengths
162
+ new_elements = after_axis ? matrix.map{ |el| [el] * after_axis }.flatten : matrix
163
+ new_elements *= before_axis if before_axis
164
+ end
165
+ NMatrix.new(new_shape, new_elements)
166
+ end
167
+ result[0], result[1] = result[1], result[0] if options[:indexing] == :xy
168
+ result
169
+ end
170
+ end
171
+
172
+ # TODO: Make this actually pretty.
173
+ def pretty_print(q) #:nodoc:
174
+ if self.shape.size > 1 and self.shape[1] > 100
175
+ self.inspect.pretty_print(q)
176
+ elsif self.dim > 3 || self.dim == 1
177
+ self.to_a.pretty_print(q)
178
+ else
179
+ # iterate through the whole matrix and find the longest number
180
+ longest = Array.new(self.shape[1], 0)
181
+ self.each_column.with_index do |col, j|
182
+ col.each do |elem|
183
+ elem_len = elem.inspect.size
184
+ longest[j] = elem_len if longest[j] < elem_len
185
+ end
186
+ end
187
+
188
+ if self.dim == 3
189
+ q.group(0, "\n{ layers:", "}") do
190
+ self.each_layer.with_index do |layer,k|
191
+ q.group(0, "\n [\n", " ]\n") do
192
+ layer.each_row.with_index do |row,i|
193
+ q.group(0, " [", "]\n") do
194
+ q.seplist(self[i,0...self.shape[1],k].to_flat_array, lambda { q.text ", "}, :each_with_index) { |v,j| q.text v.inspect.rjust(longest[j]) }
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ else # dim 2
201
+ q.group(0, "\n[\n ", "]") do
202
+ self.each_row.with_index do |row, i|
203
+ q.group(1, " [", "]\n") do
204
+ q.seplist(row.to_a, -> { q.text ", " }, :each_with_index) do |v,j|
205
+ q.text v.inspect.rjust(longest[j])
206
+ end
207
+ end
208
+ q.breakable unless i + 1 == self.shape[0]
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ #
216
+ # call-seq:
217
+ # cast(stype, dtype, default) -> NMatrix
218
+ # cast(stype, dtype) -> NMatrix
219
+ # cast(stype) -> NMatrix
220
+ # cast(options) -> NMatrix
221
+ #
222
+ # This is a user-friendly helper for calling #cast_full. The easiest way to call this function is using an
223
+ # options hash, e.g.,
224
+ #
225
+ # n.cast(:stype => :yale, :dtype => :int64, :default => false)
226
+ #
227
+ # For list and yale, :default sets the "default value" or "init" of the matrix. List allows a bit more freedom
228
+ # since non-zeros are permitted. For yale, unpredictable behavior may result if the value is not false, nil, or
229
+ # some version of 0. Dense discards :default.
230
+ #
231
+ # dtype and stype are inferred from the matrix upon which #cast is called -- so you only really need to provide
232
+ # one. You can actually call this function with no arguments, in which case it functions like #clone.
233
+ #
234
+ # If your dtype is :object and you are converting from :dense to a sparse type, it is recommended that you
235
+ # provide a :default, as 0 may behave differently from its Float or Complex equivalent. If no option
236
+ # is given, Integer 0 will be used.
237
+ def cast(*params)
238
+ if (params.size > 0 && params[0].is_a?(Hash))
239
+ opts = {
240
+ :stype => self.stype,
241
+ :dtype => self.dtype,
242
+ :default => self.stype == :dense ? 0 : self.default_value
243
+ }.merge(params[0])
244
+
245
+ self.cast_full(opts[:stype], opts[:dtype], opts[:default])
246
+ else
247
+ params << self.stype if params.size == 0
248
+ params << self.dtype if params.size == 1
249
+ #HACK: the default value can cause an exception if dtype is not complex
250
+ #and default_value is. (The ruby C code apparently won't convert these.)
251
+ #Perhaps this should be fixed in the C code (in rubyval_to_cval).
252
+ default_value = maybe_get_noncomplex_default_value(params[1])
253
+ params << (self.stype == :dense ? 0 : default_value) if params.size == 2
254
+ self.cast_full(*params)
255
+ end
256
+
257
+ end
258
+
259
+
260
+ #
261
+ # call-seq:
262
+ # rows -> Integer
263
+ #
264
+ # This shortcut use #shape to return the number of rows (the first dimension)
265
+ # of the matrix.
266
+ #
267
+ def rows
268
+ shape[0]
269
+ end
270
+
271
+ #
272
+ # call-seq:
273
+ # cols -> Integer
274
+ #
275
+ # This shortcut use #shape to return the number of columns (the second
276
+ # dimension) of the matrix.
277
+ #
278
+ def cols
279
+ shape[1]
280
+ end
281
+
282
+ # Return the main diagonal or antidiagonal a matrix. Only works with 2D matrices.
283
+ #
284
+ # == Arguments
285
+ #
286
+ # * +main_diagonal+ - Defaults to true. If passed 'false', then will return the
287
+ # antidiagonal of the matrix.
288
+ #
289
+ # == References
290
+ #
291
+ # * http://en.wikipedia.org/wiki/Main_diagonal
292
+ def diagonal main_diagonal=true
293
+ diag_size = [cols, rows].min
294
+ diag = NMatrix.new [diag_size], dtype: dtype
295
+
296
+ if main_diagonal
297
+ 0.upto(diag_size-1) do |i|
298
+ diag[i] = self[i,i]
299
+ end
300
+ else
301
+ row = 0
302
+ (diag_size-1).downto(0) do |col|
303
+ diag[row] = self[row,col]
304
+ row += 1
305
+ end
306
+ end
307
+
308
+ diag
309
+ end
310
+
311
+ #
312
+ # call-seq:
313
+ # to_hash -> Hash
314
+ #
315
+ # Create a Ruby Hash from an NMatrix.
316
+ #
317
+ def to_hash
318
+ if stype == :yale
319
+ h = {}
320
+ each_stored_with_indices do |val,i,j|
321
+ next if val == 0 # Don't bother storing the diagonal zero values -- only non-zeros.
322
+ if h.has_key?(i)
323
+ h[i][j] = val
324
+ else
325
+ h[i] = {j => val}
326
+ end
327
+ end
328
+ h
329
+ else # dense and list should use a C internal function.
330
+ # FIXME: Write a C internal to_h function.
331
+ m = stype == :dense ? self.cast(:list, self.dtype) : self
332
+ m.__list_to_hash__
333
+ end
334
+ end
335
+ alias :to_h :to_hash
336
+
337
+
338
+ def inspect #:nodoc:
339
+ original_inspect = super()
340
+ original_inspect = original_inspect[0...original_inspect.size-1]
341
+ original_inspect + " " + inspect_helper.join(" ") + ">"
342
+ end
343
+
344
+ def __yale_ary__to_s(sym) #:nodoc:
345
+ ary = self.send("__yale_#{sym.to_s}__".to_sym)
346
+
347
+ '[' + ary.collect { |a| a ? a : 'nil'}.join(',') + ']'
348
+ end
349
+
350
+
351
+ # call-seq:
352
+ # integer_dtype?() -> Boolean
353
+ #
354
+ # Checks if dtype is an integer type
355
+ #
356
+ def integer_dtype?
357
+ [:byte, :int8, :int16, :int32, :int64].include?(self.dtype)
358
+ end
359
+
360
+ # call-seq:
361
+ # float_dtype?() -> Boolean
362
+ #
363
+ # Checks if dtype is a floating point type
364
+ #
365
+ def float_dtype?
366
+ [:float32, :float64].include?(dtype)
367
+ end
368
+
369
+ ##
370
+ # call-seq:
371
+ # complex_dtype?() -> Boolean
372
+ #
373
+ # Checks if dtype is a complex type
374
+ #
375
+ def complex_dtype?
376
+ [:complex64, :complex128].include?(self.dtype)
377
+ end
378
+
379
+ ##
380
+ # call-seq:
381
+ #
382
+ # object_dtype?() -> Boolean
383
+ #
384
+ # Checks if dtype is a ruby object
385
+ def object_dtype?
386
+ dtype == :object
387
+ end
388
+
389
+
390
+ #
391
+ # call-seq:
392
+ # to_f -> Float
393
+ #
394
+ # Converts an nmatrix with a single element (but any number of dimensions)
395
+ # to a float.
396
+ #
397
+ # Raises an IndexError if the matrix does not have just a single element.
398
+ #
399
+ def to_f
400
+ raise IndexError, 'to_f only valid for matrices with a single element' unless shape.all? { |e| e == 1 }
401
+ self[*Array.new(shape.size, 0)]
402
+ end
403
+
404
+ #
405
+ # call-seq:
406
+ # to_flat_array -> Array
407
+ # to_flat_a -> Array
408
+ #
409
+ # Converts an NMatrix to a one-dimensional Ruby Array.
410
+ #
411
+ def to_flat_array
412
+ ary = Array.new(self.size)
413
+ self.each.with_index { |v,i| ary[i] = v }
414
+ ary
415
+ end
416
+ alias :to_flat_a :to_flat_array
417
+
418
+ #
419
+ # call-seq:
420
+ # size -> Integer
421
+ #
422
+ # Returns the total size of the NMatrix based on its shape.
423
+ #
424
+ def size
425
+ NMatrix.size(self.shape)
426
+ end
427
+
428
+
429
+ def to_s #:nodoc:
430
+ self.to_flat_array.to_s
431
+ end
432
+
433
+ #
434
+ # call-seq:
435
+ # nvector? -> true or false
436
+ #
437
+ # Shortcut function for determining whether the effective dimension is less than the dimension.
438
+ # Useful when we take slices of n-dimensional matrices where n > 2.
439
+ #
440
+ def nvector?
441
+ self.effective_dim < self.dim
442
+ end
443
+
444
+ #
445
+ # call-seq:
446
+ # vector? -> true or false
447
+ #
448
+ # Shortcut function for determining whether the effective dimension is 1. See also #nvector?
449
+ #
450
+ def vector?
451
+ self.effective_dim == 1
452
+ end
453
+
454
+
455
+ #
456
+ # call-seq:
457
+ # to_a -> Array
458
+ #
459
+ # Converts an NMatrix to an array of arrays, or an NMatrix of effective dimension 1 to an array.
460
+ #
461
+ # Does not yet work for dimensions > 2
462
+ def to_a(dimen=nil)
463
+ if self.dim == 2
464
+
465
+ return self.to_flat_a if self.shape[0] == 1
466
+
467
+ ary = []
468
+ begin
469
+ self.each_row do |row|
470
+ ary << row.to_flat_a
471
+ end
472
+ #rescue NotImplementedError # Oops. Try copying instead
473
+ # self.each_row(:copy) do |row|
474
+ # ary << row.to_a.flatten
475
+ # end
476
+ end
477
+ ary
478
+ else
479
+ to_a_rec(0)
480
+ end
481
+ end
482
+
483
+
484
+ #
485
+ # call-seq:
486
+ # rank(dimension, row_or_column_number) -> NMatrix
487
+ # rank(dimension, row_or_column_number, :reference) -> NMatrix reference slice
488
+ #
489
+ # Returns the rank (e.g., row, column, or layer) specified, using slicing by copy as default.
490
+ #
491
+ # See @row (dimension = 0), @column (dimension = 1)
492
+ def rank(shape_idx, rank_idx, meth = :copy)
493
+
494
+ if shape_idx > (self.dim-1)
495
+ raise(RangeError, "#rank call was out of bounds")
496
+ end
497
+
498
+ params = Array.new(self.dim)
499
+ params.each.with_index do |v,d|
500
+ params[d] = d == shape_idx ? rank_idx : 0...self.shape[d]
501
+ end
502
+
503
+ meth == :reference ? self[*params] : self.slice(*params)
504
+ end
505
+
506
+ #
507
+ # call-seq:
508
+ # column(column_number) -> NMatrix
509
+ # column(column_number, get_by) -> NMatrix
510
+ #
511
+ # Returns the column specified. Uses slicing by copy as default.
512
+ #
513
+ # * *Arguments* :
514
+ # - +column_number+ -> Integer.
515
+ # - +get_by+ -> Type of slicing to use, +:copy+ or +:reference+.
516
+ # * *Returns* :
517
+ # - A NMatrix representing the requested column as a column vector.
518
+ #
519
+ # Examples:
520
+ #
521
+ # m = NMatrix.new(2, [1, 4, 9, 14], :int32) # => 1 4
522
+ # 9 14
523
+ #
524
+ # m.column(1) # => 4
525
+ # 14
526
+ #
527
+ def column(column_number, get_by = :copy)
528
+ rank(1, column_number, get_by)
529
+ end
530
+
531
+ alias :col :column
532
+
533
+ #
534
+ # call-seq:
535
+ # row(row_number) -> NMatrix
536
+ # row(row_number, get_by) -> NMatrix
537
+ #
538
+ # * *Arguments* :
539
+ # - +row_number+ -> Integer.
540
+ # - +get_by+ -> Type of slicing to use, +:copy+ or +:reference+.
541
+ # * *Returns* :
542
+ # - An NMatrix representing the requested row as a row vector.
543
+ #
544
+ def row(row_number, get_by = :copy)
545
+ rank(0, row_number, get_by)
546
+ end
547
+
548
+ #
549
+ # call-seq:
550
+ # last -> Element of self.dtype
551
+ #
552
+ # Returns the last element stored in an NMatrix
553
+ #
554
+ def last
555
+ self[*Array.new(self.dim, -1)]
556
+ end
557
+
558
+
559
+ #
560
+ # call-seq:
561
+ # reshape(new_shape) -> NMatrix
562
+ #
563
+ # Clone a matrix, changing the shape in the process. Note that this function does not do a resize; the product of
564
+ # the new and old shapes' components must be equal.
565
+ #
566
+ # * *Arguments* :
567
+ # - +new_shape+ -> Array of positive Integers.
568
+ # * *Returns* :
569
+ # - A copy with a different shape.
570
+ #
571
+ def reshape new_shape,*shapes
572
+ if new_shape.is_a?Integer
573
+ newer_shape = [new_shape]+shapes
574
+ else # new_shape is an Array
575
+ newer_shape = new_shape
576
+ end
577
+ t = reshape_clone_structure(newer_shape)
578
+ left_params = [:*]*newer_shape.size
579
+ right_params = [:*]*self.shape.size
580
+ t[*left_params] = self[*right_params]
581
+ t
582
+ end
583
+
584
+
585
+ #
586
+ # call-seq:
587
+ # reshape!(new_shape) -> NMatrix
588
+ # reshape! new_shape -> NMatrix
589
+ #
590
+ # Reshapes the matrix (in-place) to the desired shape. Note that this function does not do a resize; the product of
591
+ # the new and old shapes' components must be equal.
592
+ #
593
+ # * *Arguments* :
594
+ # - +new_shape+ -> Array of positive Integer.
595
+ #
596
+ def reshape! new_shape,*shapes
597
+ if self.is_ref?
598
+ raise(ArgumentError, "This operation cannot be performed on reference slices")
599
+ else
600
+ if new_shape.is_a?Integer
601
+ shape = [new_shape]+shapes
602
+ else # new_shape is an Array
603
+ shape = new_shape
604
+ end
605
+ self.reshape_bang(shape)
606
+ end
607
+ end
608
+
609
+ #
610
+ # call-seq:
611
+ # transpose -> NMatrix
612
+ # transpose(permutation) -> NMatrix
613
+ #
614
+ # Clone a matrix, transposing it in the process. If the matrix is two-dimensional, the permutation is taken to be [1,0]
615
+ # automatically (switch dimension 0 with dimension 1). If the matrix is n-dimensional, you must provide a permutation
616
+ # of +0...n+.
617
+ #
618
+ # * *Arguments* :
619
+ # - +permutation+ -> Optional Array giving a permutation.
620
+ # * *Returns* :
621
+ # - A copy of the matrix, but transposed.
622
+ #
623
+ def transpose(permute = nil)
624
+ if permute.nil?
625
+ if self.dim == 1
626
+ return self.clone
627
+ elsif self.dim == 2
628
+ new_shape = [self.shape[1], self.shape[0]]
629
+ else
630
+ raise(ArgumentError, "need permutation array of size #{self.dim}")
631
+ end
632
+ elsif !permute.is_a?(Array) || permute.sort.uniq != (0...self.dim).to_a
633
+ raise(ArgumentError, "invalid permutation array")
634
+ else
635
+ # Figure out the new shape based on the permutation given as an argument.
636
+ new_shape = permute.map { |p| self.shape[p] }
637
+ end
638
+
639
+ if self.dim > 2 # FIXME: For dense, several of these are basically equivalent to reshape.
640
+
641
+ # Make the new data structure.
642
+ t = self.reshape_clone_structure(new_shape)
643
+
644
+ self.each_stored_with_indices do |v,*indices|
645
+ p_indices = permute.map { |p| indices[p] }
646
+ t[*p_indices] = v
647
+ end
648
+ t
649
+ elsif self.list? # TODO: Need a C list transposition algorithm.
650
+ # Make the new data structure.
651
+ t = self.reshape_clone_structure(new_shape)
652
+
653
+ self.each_column.with_index do |col,j|
654
+ t[j,:*] = col.to_flat_array
655
+ end
656
+ t
657
+ else
658
+ # Call C versions of Yale and List transpose, which do their own copies
659
+ if jruby?
660
+ nmatrix = NMatrix.new :copy
661
+ nmatrix.shape = [@shape[1],@shape[0]]
662
+ twoDMat = self.twoDMat.transpose
663
+ nmatrix.s = ArrayRealVector.new(ArrayGenerator.getArrayDouble(twoDMat.getData(), shape[1],shape[0]))
664
+ return nmatrix
665
+ else
666
+ self.clone_transpose
667
+ end
668
+ end
669
+ end
670
+
671
+
672
+ # call-seq:
673
+ # matrix1.concat(*m2) -> NMatrix
674
+ # matrix1.concat(*m2, rank) -> NMatrix
675
+ # matrix1.hconcat(*m2) -> NMatrix
676
+ # matrix1.vconcat(*m2) -> NMatrix
677
+ # matrix1.dconcat(*m3) -> NMatrix
678
+ #
679
+ # Joins two matrices together into a new larger matrix. Attempts to determine
680
+ # which direction to concatenate on by looking for the first common element
681
+ # of the matrix +shape+ in reverse. In other words, concatenating two columns
682
+ # together without supplying +rank+ will glue them into an n x 2 matrix.
683
+ #
684
+ # You can also use hconcat, vconcat, and dconcat for the first three ranks.
685
+ # concat performs an hconcat when no rank argument is provided.
686
+ #
687
+ # The two matrices must have the same +dim+.
688
+ #
689
+ # * *Arguments* :
690
+ # - +matrices+ -> one or more matrices
691
+ # - +rank+ -> Integer (for rank); alternatively, may use :row, :column, or
692
+ # :layer for 0, 1, 2, respectively
693
+ def concat(*matrices)
694
+ rank = nil
695
+ rank = matrices.pop unless matrices.last.is_a?(NMatrix)
696
+
697
+ # Find the first matching dimension and concatenate along that (unless rank is specified)
698
+ if rank.nil?
699
+ rank = self.dim-1
700
+ self.shape.reverse_each.with_index do |s,i|
701
+ matrices.each do |m|
702
+ if m.shape[i] != s
703
+ rank -= 1
704
+ break
705
+ end
706
+ end
707
+ end
708
+ elsif rank.is_a?(Symbol) # Convert to numeric
709
+ rank = {:row => 0, :column => 1, :col => 1, :lay => 2, :layer => 2}[rank]
710
+ end
711
+
712
+ # Need to figure out the new shape.
713
+ new_shape = self.shape.dup
714
+ new_shape[rank] = matrices.inject(self.shape[rank]) { |total,m| total + m.shape[rank] }
715
+
716
+ # Now figure out the options for constructing the concatenated matrix.
717
+ opts = {stype: self.stype, default: self.default_value, dtype: self.dtype}
718
+ if self.yale?
719
+ # We can generally predict the new capacity for Yale. Subtract out the number of rows
720
+ # for each matrix being concatenated, and then add in the number of rows for the new
721
+ # shape. That takes care of the diagonal. The rest of the capacity is represented by
722
+ # the non-diagonal non-default values.
723
+ new_cap = matrices.inject(self.capacity - self.shape[0]) do |total,m|
724
+ total + m.capacity - m.shape[0]
725
+ end - self.shape[0] + new_shape[0]
726
+ opts = {capacity: new_cap}.merge(opts)
727
+ end
728
+
729
+ # Do the actual construction.
730
+ n = NMatrix.new(new_shape, opts)
731
+
732
+ # Figure out where to start concatenation. We don't know where it will end,
733
+ # because each matrix may have own size along concat dimension.
734
+ pos = Array.new(self.dim) { 0 }
735
+
736
+ matrices.unshift(self)
737
+ matrices.each do |m|
738
+ # Figure out where to start and stop the concatenation. We'll use
739
+ # NMatrices instead of Arrays because then we can do elementwise addition.
740
+ ranges = m.shape.map.with_index { |s,i| pos[i]...(pos[i] + s) }
741
+
742
+ n[*ranges] = m
743
+
744
+ # Move over by the requisite amount
745
+ pos[rank] = pos[rank] + m.shape[rank]
746
+ end
747
+
748
+ n
749
+ end
750
+
751
+ # Horizontal concatenation with +matrices+.
752
+ def hconcat(*matrices)
753
+ concat(*matrices, :column)
754
+ end
755
+
756
+ # Vertical concatenation with +matrices+.
757
+ def vconcat(*matrices)
758
+ concat(*matrices, :row)
759
+ end
760
+
761
+ # Depth concatenation with +matrices+.
762
+ def dconcat(*matrices)
763
+ concat(*matrices, :layer)
764
+ end
765
+
766
+
767
+ #
768
+ # call-seq:
769
+ # upper_triangle -> NMatrix
770
+ # upper_triangle(k) -> NMatrix
771
+ # triu -> NMatrix
772
+ # triu(k) -> NMatrix
773
+ #
774
+ # Returns the upper triangular portion of a matrix. This is analogous to the +triu+ method
775
+ # in MATLAB.
776
+ #
777
+ # * *Arguments* :
778
+ # - +k+ -> Positive integer. How many extra diagonals to include in the upper triangular portion.
779
+ #
780
+ def upper_triangle(k = 0)
781
+ raise(NotImplementedError, "only implemented for 2D matrices") if self.shape.size > 2
782
+
783
+ t = self.clone_structure
784
+ (0...self.shape[0]).each do |i|
785
+ if i - k < 0
786
+ t[i, :*] = self[i, :*]
787
+ else
788
+ t[i, 0...(i-k)] = 0
789
+ t[i, (i-k)...self.shape[1]] = self[i, (i-k)...self.shape[1]]
790
+ end
791
+ end
792
+ t
793
+ end
794
+ alias :triu :upper_triangle
795
+
796
+
797
+ #
798
+ # call-seq:
799
+ # upper_triangle! -> NMatrix
800
+ # upper_triangle!(k) -> NMatrix
801
+ # triu! -> NMatrix
802
+ # triu!(k) -> NMatrix
803
+ #
804
+ # Deletes the lower triangular portion of the matrix (in-place) so only the upper portion remains.
805
+ #
806
+ # * *Arguments* :
807
+ # - +k+ -> Integer. How many extra diagonals to include in the deletion.
808
+ #
809
+ def upper_triangle!(k = 0)
810
+ raise(NotImplementedError, "only implemented for 2D matrices") if self.shape.size > 2
811
+
812
+ (0...self.shape[0]).each do |i|
813
+ if i - k >= 0
814
+ self[i, 0...(i-k)] = 0
815
+ end
816
+ end
817
+ self
818
+ end
819
+ alias :triu! :upper_triangle!
820
+
821
+
822
+ #
823
+ # call-seq:
824
+ # lower_triangle -> NMatrix
825
+ # lower_triangle(k) -> NMatrix
826
+ # tril -> NMatrix
827
+ # tril(k) -> NMatrix
828
+ #
829
+ # Returns the lower triangular portion of a matrix. This is analogous to the +tril+ method
830
+ # in MATLAB.
831
+ #
832
+ # * *Arguments* :
833
+ # - +k+ -> Integer. How many extra diagonals to include in the lower triangular portion.
834
+ #
835
+ def lower_triangle(k = 0)
836
+ raise(NotImplementedError, "only implemented for 2D matrices") if self.shape.size > 2
837
+
838
+ t = self.clone_structure
839
+ (0...self.shape[0]).each do |i|
840
+ if i + k >= shape[0]
841
+ t[i, :*] = self[i, :*]
842
+ else
843
+ t[i, (i+k+1)...self.shape[1]] = 0
844
+ t[i, 0..(i+k)] = self[i, 0..(i+k)]
845
+ end
846
+ end
847
+ t
848
+ end
849
+ alias :tril :lower_triangle
850
+
851
+
852
+ #
853
+ # call-seq:
854
+ # lower_triangle! -> NMatrix
855
+ # lower_triangle!(k) -> NMatrix
856
+ # tril! -> NMatrix
857
+ # tril!(k) -> NMatrix
858
+ #
859
+ # Deletes the upper triangular portion of the matrix (in-place) so only the lower portion remains.
860
+ #
861
+ # * *Arguments* :
862
+ # - +k+ -> Integer. How many extra diagonals to include in the deletion.
863
+ #
864
+ def lower_triangle!(k = 0)
865
+ raise(NotImplementedError, "only implemented for 2D matrices") if self.shape.size > 2
866
+
867
+ (0...self.shape[0]).each do |i|
868
+ if i + k < shape[0]
869
+ self[i, (i+k+1)...self.shape[1]] = 0
870
+ end
871
+ end
872
+ self
873
+ end
874
+ alias :tril! :lower_triangle!
875
+
876
+
877
+ #
878
+ # call-seq:
879
+ # layer(layer_number) -> NMatrix
880
+ # row(layer_number, get_by) -> NMatrix
881
+ #
882
+ # * *Arguments* :
883
+ # - +layer_number+ -> Integer.
884
+ # - +get_by+ -> Type of slicing to use, +:copy+ or +:reference+.
885
+ # * *Returns* :
886
+ # - A NMatrix representing the requested layer as a layer vector.
887
+ #
888
+ def layer(layer_number, get_by = :copy)
889
+ layer = rank(2, layer_number, get_by)
890
+
891
+ if jruby?
892
+ nmatrix = NMatrix.new :copy
893
+ nmatrix.shape = layer.shape
894
+ nmatrix.s = layer.s
895
+ return nmatrix
896
+ else
897
+ layer
898
+ end
899
+
900
+ end
901
+
902
+
903
+
904
+ #
905
+ # call-seq:
906
+ # shuffle! -> ...
907
+ # shuffle!(random: rng) -> ...
908
+ #
909
+ # Re-arranges the contents of an NVector.
910
+ #
911
+ # TODO: Write more efficient version for Yale, list.
912
+ # TODO: Generalize for more dimensions.
913
+ def shuffle!(*args)
914
+ method_missing(:shuffle!, *args) if self.effective_dim > 1
915
+ ary = self.to_flat_a
916
+ ary.shuffle!(*args)
917
+ ary.each.with_index { |v,idx| self[idx] = v }
918
+ self
919
+ end
920
+
921
+
922
+ #
923
+ # call-seq:
924
+ # shuffle -> ...
925
+ # shuffle(rng) -> ...
926
+ #
927
+ # Re-arranges the contents of an NVector.
928
+ #
929
+ # TODO: Write more efficient version for Yale, list.
930
+ # TODO: Generalize for more dimensions.
931
+ def shuffle(*args)
932
+ method_missing(:shuffle!, *args) if self.effective_dim > 1
933
+ t = self.clone
934
+ t.shuffle!(*args)
935
+ end
936
+
937
+
938
+ #
939
+ # call-seq:
940
+ # sorted_indices -> Array
941
+ #
942
+ # Returns an array of the indices ordered by value sorted.
943
+ #
944
+ def sorted_indices
945
+ return method_missing(:sorted_indices) unless vector?
946
+ ary = self.to_flat_array
947
+ ary.each_index.sort_by { |i| ary[i] } # from: http://stackoverflow.com/a/17841159/170300
948
+ end
949
+
950
+
951
+ #
952
+ # call-seq:
953
+ # binned_sorted_indices -> Array
954
+ #
955
+ # Returns an array of arrays of indices ordered by value sorted. Functions basically like +sorted_indices+, but
956
+ # groups indices together for those values that are the same.
957
+ #
958
+ def binned_sorted_indices
959
+ return method_missing(:sorted_indices) unless vector?
960
+ ary = self.to_flat_array
961
+ ary2 = []
962
+ last_bin = ary.each_index.sort_by { |i| [ary[i]] }.inject([]) do |result, element|
963
+ if result.empty? || ary[result[-1]] == ary[element]
964
+ result << element
965
+ else
966
+ ary2 << result
967
+ [element]
968
+ end
969
+ end
970
+ ary2 << last_bin unless last_bin.empty?
971
+ ary2
972
+ end
973
+
974
+
975
+ def method_missing name, *args, &block #:nodoc:
976
+ if name.to_s =~ /^__list_elementwise_.*__$/
977
+ raise NotImplementedError, "requested undefined list matrix element-wise operation"
978
+ elsif name.to_s =~ /^__yale_scalar_.*__$/
979
+ raise NotImplementedError, "requested undefined yale scalar element-wise operation"
980
+ else
981
+ super(name, *args, &block)
982
+ end
983
+ end
984
+
985
+
986
+ def respond_to?(method, include_all = false) #:nodoc:
987
+ if [:shuffle, :shuffle!, :each_with_index, :sorted_indices, :binned_sorted_indices, :nrm2, :asum].include?(method.intern) # vector-only methods
988
+ return vector?
989
+ elsif [:each_layer, :layer].include?(method.intern) # 3-or-more dimensions only
990
+ return dim > 2
991
+ else
992
+ super
993
+ end
994
+ end
995
+
996
+
997
+ #
998
+ # call-seq:
999
+ # inject -> symbol
1000
+ #
1001
+ # This overrides the inject function to use map_stored for yale matrices
1002
+ #
1003
+ def inject(sym)
1004
+ return super(sym) unless self.yale?
1005
+ return self.map_stored.inject(sym)
1006
+ end
1007
+
1008
+ # Returns the index of the first occurence of the specified value. Returns
1009
+ # an array containing the position of the value, nil in case the value is not found.
1010
+ #
1011
+ def index(value)
1012
+ index = nil
1013
+
1014
+ self.each_with_indices do |yields|
1015
+ if yields.first == value
1016
+ yields.shift
1017
+ index = yields
1018
+ break
1019
+ end
1020
+ end
1021
+
1022
+ index
1023
+ end
1024
+
1025
+ #
1026
+ # call-seq:
1027
+ # clone_structure -> NMatrix
1028
+ #
1029
+ # This function is like clone, but it only copies the structure and the default value.
1030
+ # None of the other values are copied. It takes an optional capacity argument. This is
1031
+ # mostly only useful for dense, where you may not want to initialize; for other types,
1032
+ # you should probably use +zeros_like+.
1033
+ #
1034
+ def clone_structure(capacity = nil)
1035
+ opts = {stype: self.stype, default: self.default_value, dtype: self.dtype}
1036
+ opts = {capacity: capacity}.merge(opts) if self.yale?
1037
+ NMatrix.new(self.shape, opts)
1038
+ end
1039
+
1040
+ #
1041
+ # call-seq:
1042
+ # repeat(count, axis) -> NMatrix
1043
+ #
1044
+ # * *Arguments* :
1045
+ # - +count+ -> how many times NMatrix should be repeated
1046
+ # - +axis+ -> index of axis along which NMatrix should be repeated
1047
+ # * *Returns* :
1048
+ # - NMatrix created by repeating the existing one along an axis
1049
+ # * *Examples* :
1050
+ # m = NMatrix.new([2, 2], [1, 2, 3, 4])
1051
+ # m.repeat(2, 0).to_a #<= [[1, 2], [3, 4], [1, 2], [3, 4]]
1052
+ # m.repeat(2, 1).to_a #<= [[1, 2, 1, 2], [3, 4, 3, 4]]
1053
+ def repeat(count, axis)
1054
+ raise(ArgumentError, 'Matrix should be repeated at least 2 times.') if count < 2
1055
+ new_shape = shape
1056
+ new_shape[axis] *= count
1057
+ new_matrix = NMatrix.new(new_shape, dtype: dtype)
1058
+ slice = new_shape.map { |axis_size| 0...axis_size }
1059
+ start = 0
1060
+ count.times do
1061
+ slice[axis] = start...(start += shape[axis])
1062
+ new_matrix[*slice] = self
1063
+ end
1064
+ new_matrix
1065
+ end
1066
+
1067
+ # This is how you write an individual element-wise operation function:
1068
+ #def __list_elementwise_add__ rhs
1069
+ # self.__list_map_merged_stored__(rhs){ |l,r| l+r }.cast(self.stype, NMatrix.upcast(self.dtype, rhs.dtype))
1070
+ #end
1071
+ protected
1072
+
1073
+ def inspect_helper #:nodoc:
1074
+ ary = []
1075
+ ary << "shape:[#{shape.join(',')}]" << "dtype:#{dtype}" << "stype:#{stype}"
1076
+
1077
+ if stype == :yale
1078
+ ary << "capacity:#{capacity}"
1079
+
1080
+ # These are enabled by the DEBUG_YALE compiler flag in extconf.rb.
1081
+ if respond_to?(:__yale_a__)
1082
+ ary << "ija:#{__yale_ary__to_s(:ija)}" << "ia:#{__yale_ary__to_s(:ia)}" <<
1083
+ "ja:#{__yale_ary__to_s(:ja)}" << "a:#{__yale_ary__to_s(:a)}" << "d:#{__yale_ary__to_s(:d)}" <<
1084
+ "lu:#{__yale_ary__to_s(:lu)}" << "yale_size:#{__yale_size__}"
1085
+ end
1086
+
1087
+ end
1088
+
1089
+ ary
1090
+ end
1091
+
1092
+
1093
+ # Clone the structure as needed for a reshape
1094
+ def reshape_clone_structure(new_shape) #:nodoc:
1095
+ raise(ArgumentError, "reshape cannot resize; size of new and old matrices must match") unless self.size == new_shape.inject(1) { |p,i| p *= i }
1096
+
1097
+ opts = {stype: self.stype, default: self.default_value, dtype: self.dtype}
1098
+ if self.yale?
1099
+ # We can generally predict the change in capacity for Yale.
1100
+ opts = {capacity: self.capacity - self.shape[0] + new_shape[0]}.merge(opts)
1101
+ end
1102
+ NMatrix.new(new_shape, opts)
1103
+ end
1104
+
1105
+
1106
+ # Helper for converting a matrix into an array of arrays recursively
1107
+ def to_a_rec(dimen = 0) #:nodoc:
1108
+ return self.flat_map { |v| v } if dimen == self.dim-1
1109
+
1110
+ ary = []
1111
+ self.each_rank(dimen) do |sect|
1112
+ ary << sect.to_a_rec(dimen+1)
1113
+ end
1114
+ ary
1115
+ end
1116
+
1117
+
1118
+ # NMatrix constructor helper for sparse matrices. Uses multi-slice-setting to initialize a matrix
1119
+ # with a given array of initial values.
1120
+ def __sparse_initial_set__(ary) #:nodoc:
1121
+ self[0...self.shape[0],0...self.shape[1]] = ary
1122
+ end
1123
+
1124
+
1125
+ # This function assumes that the shapes of the two matrices have already
1126
+ # been tested and are the same.
1127
+ #
1128
+ # Called from inside NMatrix: nm_eqeq
1129
+ #
1130
+ # There are probably more efficient ways to do this, but currently it's unclear how.
1131
+ # We could use +each_row+, but for list matrices, it's still going to need to make a
1132
+ # reference to each of those rows, and that is going to require a seek.
1133
+ #
1134
+ # It might be more efficient to convert one sparse matrix type to the other with a
1135
+ # cast and then run the comparison. For now, let's assume that people aren't going
1136
+ # to be doing this very often, and we can optimize as needed.
1137
+ def dense_eql_sparse? m #:nodoc:
1138
+ m.each_with_indices do |v,*indices|
1139
+ return false if self[*indices] != v
1140
+ end
1141
+
1142
+ return true
1143
+ end
1144
+ alias :sparse_eql_sparse? :dense_eql_sparse?
1145
+
1146
+
1147
+ #
1148
+ # See the note in #cast about why this is necessary.
1149
+ # If this is a non-dense matrix with a complex dtype and to_dtype is
1150
+ # non-complex, then this will convert the default value to noncomplex.
1151
+ # Returns 0 if dense. Returns existing default_value if there isn't a
1152
+ # mismatch.
1153
+ #
1154
+ def maybe_get_noncomplex_default_value(to_dtype) #:nodoc:
1155
+ default_value = 0
1156
+ unless self.stype == :dense then
1157
+ if self.dtype.to_s.start_with?('complex') and not to_dtype.to_s.start_with?('complex') then
1158
+ default_value = self.default_value.real
1159
+ else
1160
+ default_value = self.default_value
1161
+ end
1162
+ end
1163
+ default_value
1164
+ end
1165
+
1166
+ end
1167
+
1168
+ require_relative './shortcuts.rb'
1169
+ require_relative './enumerate.rb'
1170
+
1171
+ require_relative './version.rb'
1172
+ require_relative './blas.rb'