pnmatrix 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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'