nmatrix 0.1.0 → 0.2.0

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/ext/nmatrix/data/complex.h +20 -55
  3. data/ext/nmatrix/data/data.cpp +11 -44
  4. data/ext/nmatrix/data/data.h +174 -311
  5. data/ext/nmatrix/data/meta.h +1 -7
  6. data/ext/nmatrix/data/ruby_object.h +3 -85
  7. data/ext/nmatrix/extconf.rb +2 -73
  8. data/ext/nmatrix/math.cpp +170 -813
  9. data/ext/nmatrix/math/asum.h +2 -25
  10. data/ext/nmatrix/math/{inc.h → cblas_enums.h} +11 -22
  11. data/ext/nmatrix/math/cblas_templates_core.h +507 -0
  12. data/ext/nmatrix/math/gemm.h +2 -32
  13. data/ext/nmatrix/math/gemv.h +1 -35
  14. data/ext/nmatrix/math/getrf.h +21 -6
  15. data/ext/nmatrix/math/getrs.h +0 -8
  16. data/ext/nmatrix/math/imax.h +0 -22
  17. data/ext/nmatrix/math/long_dtype.h +0 -3
  18. data/ext/nmatrix/math/math.h +11 -337
  19. data/ext/nmatrix/math/nrm2.h +2 -23
  20. data/ext/nmatrix/math/rot.h +1 -25
  21. data/ext/nmatrix/math/rotg.h +4 -13
  22. data/ext/nmatrix/math/scal.h +0 -22
  23. data/ext/nmatrix/math/trsm.h +0 -55
  24. data/ext/nmatrix/math/util.h +148 -0
  25. data/ext/nmatrix/nmatrix.cpp +0 -14
  26. data/ext/nmatrix/nmatrix.h +92 -84
  27. data/ext/nmatrix/ruby_constants.cpp +0 -2
  28. data/ext/nmatrix/ruby_constants.h +0 -2
  29. data/ext/nmatrix/ruby_nmatrix.c +86 -45
  30. data/ext/nmatrix/storage/dense/dense.cpp +1 -7
  31. data/ext/nmatrix/storage/storage.h +0 -1
  32. data/ext/nmatrix/ttable_helper.rb +0 -6
  33. data/ext/nmatrix/util/io.cpp +1 -1
  34. data/lib/nmatrix.rb +1 -19
  35. data/lib/nmatrix/blas.rb +33 -11
  36. data/lib/nmatrix/io/market.rb +3 -3
  37. data/lib/nmatrix/lapack_core.rb +181 -0
  38. data/lib/nmatrix/lapack_plugin.rb +44 -0
  39. data/lib/nmatrix/math.rb +382 -131
  40. data/lib/nmatrix/monkeys.rb +2 -3
  41. data/lib/nmatrix/nmatrix.rb +166 -13
  42. data/lib/nmatrix/shortcuts.rb +72 -7
  43. data/lib/nmatrix/version.rb +2 -2
  44. data/spec/00_nmatrix_spec.rb +154 -5
  45. data/spec/02_slice_spec.rb +2 -6
  46. data/spec/03_nmatrix_monkeys_spec.rb +7 -1
  47. data/spec/blas_spec.rb +60 -33
  48. data/spec/homogeneous_spec.rb +10 -10
  49. data/spec/lapack_core_spec.rb +482 -0
  50. data/spec/math_spec.rb +436 -52
  51. data/spec/shortcuts_spec.rb +28 -4
  52. data/spec/spec_helper.rb +14 -2
  53. data/spec/utm5940.mtx +83844 -0
  54. metadata +49 -76
  55. data/.gitignore +0 -27
  56. data/.rspec +0 -2
  57. data/.travis.yml +0 -15
  58. data/CONTRIBUTING.md +0 -82
  59. data/Gemfile +0 -2
  60. data/History.txt +0 -677
  61. data/LICENSE.txt +0 -23
  62. data/Manifest.txt +0 -92
  63. data/README.rdoc +0 -150
  64. data/Rakefile +0 -216
  65. data/ext/nmatrix/data/rational.h +0 -440
  66. data/ext/nmatrix/math/geev.h +0 -82
  67. data/ext/nmatrix/math/ger.h +0 -96
  68. data/ext/nmatrix/math/gesdd.h +0 -80
  69. data/ext/nmatrix/math/gesvd.h +0 -78
  70. data/ext/nmatrix/math/getf2.h +0 -86
  71. data/ext/nmatrix/math/getri.h +0 -108
  72. data/ext/nmatrix/math/potrs.h +0 -129
  73. data/ext/nmatrix/math/swap.h +0 -52
  74. data/lib/nmatrix/lapack.rb +0 -240
  75. data/nmatrix.gemspec +0 -55
  76. data/scripts/mac-brew-gcc.sh +0 -50
  77. data/scripts/mac-mavericks-brew-gcc.sh +0 -22
  78. data/spec/lapack_spec.rb +0 -459
@@ -44,13 +44,12 @@ class Array
44
44
  # instead of :float64) -- optional.
45
45
  # <tt>stype</tt> :: Optional storage type (defaults to :dense)
46
46
  def to_nm(shape = nil, dtype = nil, stype = :dense)
47
- elements = self
47
+ elements = self.dup
48
48
 
49
49
  guess_dtype = ->(type) {
50
50
  case type
51
51
  when Fixnum then :int64
52
52
  when Float then :float64
53
- when Rational then :rational128
54
53
  when Complex then :complex128
55
54
  end
56
55
  }
@@ -75,7 +74,7 @@ class Array
75
74
  }
76
75
 
77
76
  unless shape
78
- shape = guess_shape.call(self)
77
+ shape = guess_shape.call(elements)
79
78
  elements.flatten!(shape.size - 1)
80
79
  if elements.flatten != elements
81
80
  dtype = :object
@@ -23,18 +23,33 @@
23
23
  #
24
24
  # == nmatrix.rb
25
25
  #
26
- # This file contains those core functionalities which can be
26
+ # This file loads the C extension for NMatrix and all the ruby
27
+ # files and contains those core functionalities which can be
27
28
  # implemented efficiently (or much more easily) in Ruby (e.g.,
28
29
  # inspect, pretty_print, element-wise operations).
29
30
  #++
30
31
 
31
- require_relative './lapack.rb'
32
+ # For some reason nmatrix.so ends up in a different place during gem build.
33
+ if File.exist?("lib/nmatrix/nmatrix.so") #|| File.exist?("lib/nmatrix/nmatrix.bundle")
34
+ # Development
35
+ require "nmatrix/nmatrix.so"
36
+ else
37
+ # Gem
38
+ require "nmatrix.so"
39
+ end
40
+
41
+ require_relative './io/mat_reader'
42
+ require_relative './io/mat5_reader'
43
+ require_relative './io/market'
44
+ require_relative './io/point_cloud'
45
+
46
+ require_relative './lapack_core.rb'
32
47
  require_relative './yale_functions.rb'
33
48
  require_relative './monkeys'
34
49
 
35
50
  # NMatrix is a matrix class that supports both multidimensional arrays
36
51
  # (`:dense` stype) and sparse storage (`:list` or `:yale` stypes) and 13 data
37
- # types, including complex and rational numbers, various integer and
52
+ # types, including complex numbers, various integer and
38
53
  # floating-point sizes and ruby objects.
39
54
  class NMatrix
40
55
  # Read and write extensions for NMatrix.
@@ -90,6 +105,59 @@ class NMatrix
90
105
  shape = [shape,shape] unless shape.is_a?(Array)
91
106
  (0...shape.size).inject(1) { |x,i| x * shape[i] }
92
107
  end
108
+
109
+ # Make N-D coordinate arrays for vectorized evaluations of
110
+ # N-D scalar/vector fields over N-D grids, given N
111
+ # coordinate arrays arrs. N > 1.
112
+ #
113
+ # call-seq:
114
+ # meshgrid(arrs) -> Array of NMatrix
115
+ # meshgrid(arrs, options) -> Array of NMatrix
116
+ #
117
+ # * *Arguments* :
118
+ # - +vectors+ -> Array of N coordinate arrays (Array or NMatrix), if any have more than one dimension they will be flatten
119
+ # - +options+ -> Hash with options (:sparse Boolean, false by default; :indexing Symbol, may be :ij or :xy, :xy by default)
120
+ # * *Returns* :
121
+ # - Array of N N-D NMatrixes
122
+ # * *Examples* :
123
+ # x, y = NMatrix::meshgrid([[1, [2, 3]], [4, 5]])
124
+ # x.to_a #<= [[1, 2, 3], [1, 2, 3]]
125
+ # y.to_a #<= [[4, 4, 4], [5, 5, 5]]
126
+ #
127
+ # * *Using* *options* :
128
+ #
129
+ # x, y = NMatrix::meshgrid([[[1, 2], 3], [4, 5]], sparse: true)
130
+ # x.to_a #<= [[1, 2, 3]]
131
+ # y.to_a #<= [[4], [5]]
132
+ #
133
+ # x, y = NMatrix::meshgrid([[1, 2, 3], [[4], 5]], indexing: :ij)
134
+ # x.to_a #<= [[1, 1], [2, 2], [3, 3]]
135
+ # y.to_a #<= [[4, 5], [4, 5], [4, 5]]
136
+ def meshgrid(vectors, options = {})
137
+ raise(ArgumentError, 'Expected at least 2 arrays.') if vectors.size < 2
138
+ options[:indexing] ||= :xy
139
+ raise(ArgumentError, 'Indexing must be :xy of :ij') unless [:ij, :xy].include? options[:indexing]
140
+ mats = vectors.map { |arr| arr.respond_to?(:flatten) ? arr.flatten : arr.to_flat_array }
141
+ mats[0], mats[1] = mats[1], mats[0] if options[:indexing] == :xy
142
+ new_dim = mats.size
143
+ lengths = mats.map(&:size)
144
+ result = mats.map.with_index do |matrix, axis|
145
+ if options[:sparse]
146
+ new_shape = Array.new(new_dim, 1)
147
+ new_shape[axis] = lengths[axis]
148
+ new_elements = matrix
149
+ else
150
+ before_axis = lengths[0...axis].reduce(:*)
151
+ after_axis = lengths[(axis+1)..-1].reduce(:*)
152
+ new_shape = lengths
153
+ new_elements = after_axis ? matrix.map{ |el| [el] * after_axis }.flatten : matrix
154
+ new_elements *= before_axis if before_axis
155
+ end
156
+ NMatrix.new(new_shape, new_elements)
157
+ end
158
+ result[0], result[1] = result[1], result[0] if options[:indexing] == :xy
159
+ result
160
+ end
93
161
  end
94
162
 
95
163
  # TODO: Make this actually pretty.
@@ -153,7 +221,7 @@ class NMatrix
153
221
  # one. You can actually call this function with no arguments, in which case it functions like #clone.
154
222
  #
155
223
  # If your dtype is :object and you are converting from :dense to a sparse type, it is recommended that you
156
- # provide a :default, as 0 may behave differently from its Float, Rational, or Complex equivalent. If no option
224
+ # provide a :default, as 0 may behave differently from its Float or Complex equivalent. If no option
157
225
  # is given, Fixnum 0 will be used.
158
226
  def cast(*params)
159
227
  if (params.size > 0 && params[0].is_a?(Hash))
@@ -200,6 +268,35 @@ class NMatrix
200
268
  shape[1]
201
269
  end
202
270
 
271
+ # Return the main diagonal or antidiagonal a matrix. Only works with 2D matrices.
272
+ #
273
+ # == Arguments
274
+ #
275
+ # * +main_diagonal+ - Defaults to true. If passed 'false', then will return the
276
+ # antidiagonal of the matrix.
277
+ #
278
+ # == References
279
+ #
280
+ # * http://en.wikipedia.org/wiki/Main_diagonal
281
+ def diagonal main_diagonal=true
282
+ diag_size = [cols, rows].min
283
+ diag = NMatrix.new [diag_size], dtype: dtype
284
+
285
+ if main_diagonal
286
+ 0.upto(diag_size-1) do |i|
287
+ diag[i] = self[i,i]
288
+ end
289
+ else
290
+ row = 0
291
+ (diag_size-1).downto(0) do |col|
292
+ diag[row] = self[row,col]
293
+ row += 1
294
+ end
295
+ end
296
+
297
+ diag
298
+ end
299
+
203
300
  #
204
301
  # call-seq:
205
302
  # to_hash -> Hash
@@ -249,6 +346,15 @@ class NMatrix
249
346
  [:byte, :int8, :int16, :int32, :int64].include?(self.dtype)
250
347
  end
251
348
 
349
+ # call-seq:
350
+ # float_dtype?() -> Boolean
351
+ #
352
+ # Checks if dtype is a floating point type
353
+ #
354
+ def float_dtype?
355
+ [:float32, :float64].include?(dtype)
356
+ end
357
+
252
358
  ##
253
359
  # call-seq:
254
360
  # complex_dtype?() -> Boolean
@@ -261,12 +367,12 @@ class NMatrix
261
367
 
262
368
  ##
263
369
  # call-seq:
264
- # complex_dtype?() -> Boolean
265
- #
266
- # Checks if dtype is a rational type
267
- #
268
- def rational_dtype?
269
- [:rational32, :rational64, :rational128].include?(self.dtype)
370
+ #
371
+ # object_dtype?() -> Boolean
372
+ #
373
+ # Checks if dtype is a ruby object
374
+ def object_dtype?
375
+ dtype == :object
270
376
  end
271
377
 
272
378
 
@@ -832,13 +938,13 @@ class NMatrix
832
938
  end
833
939
 
834
940
 
835
- def respond_to?(method) #:nodoc:
941
+ def respond_to?(method, include_all = false) #:nodoc:
836
942
  if [:shuffle, :shuffle!, :each_with_index, :sorted_indices, :binned_sorted_indices, :nrm2, :asum].include?(method.intern) # vector-only methods
837
943
  return vector?
838
944
  elsif [:each_layer, :layer].include?(method.intern) # 3-or-more dimensions only
839
945
  return dim > 2
840
946
  else
841
- super(method)
947
+ super
842
948
  end
843
949
  end
844
950
 
@@ -854,6 +960,22 @@ class NMatrix
854
960
  return self.map_stored.inject(sym)
855
961
  end
856
962
 
963
+ # Returns the index of the first occurence of the specified value. Returns
964
+ # an array containing the position of the value, nil in case the value is not found.
965
+ #
966
+ def index(value)
967
+ index = nil
968
+
969
+ self.each_with_indices do |yields|
970
+ if yields.first == value
971
+ yields.shift
972
+ index = yields
973
+ break
974
+ end
975
+ end
976
+
977
+ index
978
+ end
857
979
 
858
980
  #
859
981
  # call-seq:
@@ -870,6 +992,33 @@ class NMatrix
870
992
  NMatrix.new(self.shape, opts)
871
993
  end
872
994
 
995
+ #
996
+ # call-seq:
997
+ # repeat(count, axis) -> NMatrix
998
+ #
999
+ # * *Arguments* :
1000
+ # - +count+ -> how many times NMatrix should be repeated
1001
+ # - +axis+ -> index of axis along which NMatrix should be repeated
1002
+ # * *Returns* :
1003
+ # - NMatrix created by repeating the existing one along an axis
1004
+ # * *Examples* :
1005
+ # m = NMatrix.new([2, 2], [1, 2, 3, 4])
1006
+ # m.repeat(2, 0).to_a #<= [[1, 2], [3, 4], [1, 2], [3, 4]]
1007
+ # m.repeat(2, 1).to_a #<= [[1, 2, 1, 2], [3, 4, 3, 4]]
1008
+ def repeat(count, axis)
1009
+ raise(ArgumentError, 'Matrix should be repeated at least 2 times.') if count < 2
1010
+ new_shape = shape
1011
+ new_shape[axis] *= count
1012
+ new_matrix = NMatrix.new(new_shape)
1013
+ slice = new_shape.map { |axis_size| 0...axis_size }
1014
+ start = 0
1015
+ count.times do
1016
+ slice[axis] = start...(start += shape[axis])
1017
+ new_matrix[*slice] = self
1018
+ end
1019
+ new_matrix
1020
+ end
1021
+
873
1022
  # This is how you write an individual element-wise operation function:
874
1023
  #def __list_elementwise_add__ rhs
875
1024
  # self.__list_map_merged_stored__(rhs){ |l,r| l+r }.cast(self.stype, NMatrix.upcast(self.dtype, rhs.dtype))
@@ -928,7 +1077,8 @@ protected
928
1077
  end
929
1078
 
930
1079
 
931
- # Function assumes the dimensions and such have already been tested.
1080
+ # This function assumes that the shapes of the two matrices have already
1081
+ # been tested and are the same.
932
1082
  #
933
1083
  # Called from inside NMatrix: nm_eqeq
934
1084
  #
@@ -973,3 +1123,6 @@ end
973
1123
  require_relative './shortcuts.rb'
974
1124
  require_relative './math.rb'
975
1125
  require_relative './enumerate.rb'
1126
+
1127
+ require_relative './version.rb'
1128
+ require_relative './blas.rb'
@@ -59,7 +59,6 @@ class NMatrix
59
59
  #
60
60
  # The default value for +dtype+ is guessed from the first parameter. For example:
61
61
  # NMatrix[1.0, 2.0].dtype # => :float64
62
- # NMatrix[1r, 2r].dtype # => :rational64
63
62
  #
64
63
  # But this is just a *guess*. If the other values can't be converted to
65
64
  # this dtype, a +TypeError+ will be raised.
@@ -275,6 +274,76 @@ class NMatrix
275
274
  alias :diag :diagonal
276
275
  alias :diagonals :diagonal
277
276
 
277
+ # Generate a block-diagonal NMatrix from the supplied 2D square matrices.
278
+ #
279
+ # * *Arguments*
280
+ # - +*params+ -> An array that collects all arguments passed to the method. The method
281
+ # can receive any number of arguments. Optionally, the last entry of +params+ is
282
+ # a hash of options from NMatrix#initialize. All other entries of +params+ are
283
+ # the blocks of the desired block-diagonal matrix. Each such matrix block can be
284
+ # supplied as a square 2D NMatrix object, or alternatively as an array of arrays
285
+ # (with dimensions corresponding to a square matrix), or alternatively as a number.
286
+ # * *Returns*
287
+ # - NMatrix of block-diagonal form filled with specified matrices
288
+ # as the blocks along the diagonal.
289
+ #
290
+ # * *Example*
291
+ #
292
+ # a = NMatrix.new([2,2], [1,2,3,4])
293
+ # b = NMatrix.new([1,1], [123], dtype: :float64)
294
+ # c = Array.new(2) { [[10,10], [10,10]] }
295
+ # d = Array[[1,2,3], [4,5,6], [7,8,9]]
296
+ # m = NMatrix.block_diagonal(a, b, *c, d, 10.0, 11, dtype: :int64, stype: :yale)
297
+ # =>
298
+ # [
299
+ # [1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
300
+ # [3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
301
+ # [0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0]
302
+ # [0, 0, 0, 10, 10, 0, 0, 0, 0, 0, 0, 0]
303
+ # [0, 0, 0, 10, 10, 0, 0, 0, 0, 0, 0, 0]
304
+ # [0, 0, 0, 0, 0, 10, 10, 0, 0, 0, 0, 0]
305
+ # [0, 0, 0, 0, 0, 10, 10, 0, 0, 0, 0, 0]
306
+ # [0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0]
307
+ # [0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 0, 0]
308
+ # [0, 0, 0, 0, 0, 0, 0, 7, 8, 9, 0, 0]
309
+ # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0]
310
+ # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11]
311
+ # ]
312
+ #
313
+ def block_diagonal(*params)
314
+ options = params.last.is_a?(Hash) ? params.pop : {}
315
+
316
+ params.each_index do |i|
317
+ params[i] = params[i].to_nm if params[i].is_a?(Array) # Convert Array to NMatrix
318
+ params[i] = NMatrix.new([1,1], [params[i]]) if params[i].is_a?(Numeric) # Convert number to NMatrix
319
+ end
320
+
321
+ block_sizes = [] #holds the size of each matrix block
322
+ params.each do |b|
323
+ unless b.is_a?(NMatrix)
324
+ raise(ArgumentError, "Only NMatrix or appropriate Array objects or single numbers allowed")
325
+ end
326
+ raise(ArgumentError, "Only 2D matrices or 2D arrays allowed") unless b.shape.size == 2
327
+ raise(ArgumentError, "Only square-shaped blocks allowed") unless b.shape[0] == b.shape[1]
328
+ block_sizes << b.shape[0]
329
+ end
330
+
331
+ block_diag_mat = NMatrix.zeros(block_sizes.sum, options)
332
+ (0...params.length).each do |n|
333
+ # First determine the size and position of the n'th block in the block-diagonal matrix
334
+ block_size = block_sizes[n]
335
+ block_pos = block_sizes[0...n].sum
336
+ # populate the n'th block in the block-diagonal matrix
337
+ (0...block_size).each do |i|
338
+ (0...block_size).each do |j|
339
+ block_diag_mat[block_pos+i,block_pos+j] = params[n][i,j]
340
+ end
341
+ end
342
+ end
343
+
344
+ return block_diag_mat
345
+ end
346
+ alias :block_diag :block_diagonal
278
347
 
279
348
  #
280
349
  # call-seq:
@@ -284,8 +353,7 @@ class NMatrix
284
353
  # by +Random::rand+. The parameter is the dimension of the matrix.
285
354
  #
286
355
  # If you use an integer dtype, make sure to specify :scale as a parameter, or you'll
287
- # only get a matrix of 0s. You may not currently generate random numbers for
288
- # a rational matrix.
356
+ # only get a matrix of 0s.
289
357
  #
290
358
  # * *Arguments* :
291
359
  # - +shape+ -> Array (or integer for square matrix) specifying the dimensions.
@@ -302,8 +370,6 @@ class NMatrix
302
370
  def random(shape, opts={})
303
371
  scale = opts.delete(:scale) || 1.0
304
372
 
305
- raise(NotImplementedError, "does not support rational random number generation") if opts[:dtype].to_s =~ /^rational/
306
-
307
373
  rng = Random.new
308
374
 
309
375
  random_values = []
@@ -330,7 +396,6 @@ class NMatrix
330
396
  # dindgen(shape) -> NMatrix of :float64
331
397
  # cindgen(shape) -> NMatrix of :complex64
332
398
  # zindgen(shape) -> NMatrix of :complex128
333
- # rindgen(shape) -> NMatrix of :rational128
334
399
  # rbindgen(shape) -> NMatrix of :object
335
400
  #
336
401
  # Creates a matrix filled with a sequence of integers starting at zero.
@@ -361,7 +426,7 @@ class NMatrix
361
426
 
362
427
  {:bindgen => :byte, :indgen => :int64, :findgen => :float32, :dindgen => :float64,
363
428
  :cindgen => :complex64, :zindgen => :complex128,
364
- :rindgen => :rational128, :rbindgen => :object}.each_pair do |meth, dtype|
429
+ :rbindgen => :object}.each_pair do |meth, dtype|
365
430
  define_method(meth) { |shape| NMatrix.seq(shape, :dtype => dtype) }
366
431
  end
367
432
  end
@@ -28,9 +28,9 @@ class NMatrix
28
28
  # IO can still understand NMatrix::VERSION.
29
29
  module VERSION #:nodoc:
30
30
  MAJOR = 0
31
- MINOR = 1
31
+ MINOR = 2
32
32
  TINY = 0
33
- # PRE = "rc5"
33
+ #PRE = "a"
34
34
 
35
35
  STRING = [MAJOR, MINOR, TINY].compact.join(".")
36
36
  #STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -478,13 +478,36 @@ describe 'NMatrix' do
478
478
  context "#==" do
479
479
  [:dense, :list, :yale].each do |left|
480
480
  [:dense, :list, :yale].each do |right|
481
- next if left == right
482
481
  context ("#{left}?#{right}") do
483
- it "should compare two matrices of differing stypes" do
484
- n = NMatrix.new([3,4], [0,0,1,2,0,0,3,4,0,0,0,0,5,6,7,0], stype: left)
485
- m = NMatrix.new([3,4], [0,0,1,2,0,0,3,4,0,0,0,0,5,6,7,0], stype: right)
486
- expect(n).to eq(m)
482
+ it "tests equality of two equal matrices" do
483
+ n = NMatrix.new([3,4], [0,0,1,2,0,0,3,4,0,0,0,0], stype: left)
484
+ m = NMatrix.new([3,4], [0,0,1,2,0,0,3,4,0,0,0,0], stype: right)
485
+
486
+ expect(n==m).to eq(true)
487
+ end
488
+
489
+ it "tests equality of two unequal matrices" do
490
+ n = NMatrix.new([3,4], [0,0,1,2,0,0,3,4,0,0,0,1], stype: left)
491
+ m = NMatrix.new([3,4], [0,0,1,2,0,0,3,4,0,0,0,0], stype: right)
492
+
493
+ expect(n==m).to eq(false)
494
+ end
495
+
496
+ it "tests equality of matrices with different shapes" do
497
+ n = NMatrix.new([2,2], [1,2, 3,4], stype: left)
498
+ m = NMatrix.new([2,3], [1,2, 3,4, 5,6], stype: right)
499
+ x = NMatrix.new([1,4], [1,2, 3,4], stype: right)
500
+
501
+ expect{n==m}.to raise_error(ShapeError)
502
+ expect{n==x}.to raise_error(ShapeError)
487
503
  end
504
+
505
+ it "tests equality of matrices with different dimension" do
506
+ n = NMatrix.new([2,1], [1,2], stype: left)
507
+ m = NMatrix.new([2], [1,2], stype: right)
508
+
509
+ expect{n==m}.to raise_error(ShapeError)
510
+ end if left != :yale && right != :yale # yale must have dimension 2
488
511
  end
489
512
  end
490
513
  end
@@ -578,4 +601,130 @@ describe 'NMatrix' do
578
601
  end
579
602
  end
580
603
 
604
+ context "#index" do
605
+ it "returns index of first occurence of an element for a vector" do
606
+ n = NMatrix.new([5], [0,22,22,11,11])
607
+
608
+ expect(n.index(22)).to eq([1])
609
+ end
610
+
611
+ it "returns index of first occurence of an element for 2-D matrix" do
612
+ n = NMatrix.new([3,3], [23,11,23,
613
+ 44, 2, 0,
614
+ 33, 0, 32])
615
+
616
+ expect(n.index(0)).to eq([1,2])
617
+ end
618
+
619
+ it "returns index of first occerence of an element for N-D matrix" do
620
+ n = NMatrix.new([3,3,3], [23,11,23, 44, 2, 0, 33, 0, 32,
621
+ 23,11,23, 44, 2, 0, 33, 0, 32,
622
+ 23,11,23, 44, 2, 0, 33, 0, 32])
623
+
624
+ expect(n.index(44)).to eq([0,1,0])
625
+ end
626
+ end
627
+
628
+ context "#diagonal" do
629
+ ALL_DTYPES.each do |dtype|
630
+ before do
631
+ @square_matrix = NMatrix.new([3,3], [
632
+ 23,11,23,
633
+ 44, 2, 0,
634
+ 33, 0, 32
635
+ ], dtype: dtype
636
+ )
637
+
638
+ @rect_matrix = NMatrix.new([4,3], [
639
+ 23,11,23,
640
+ 44, 2, 0,
641
+ 33, 0,32,
642
+ 11,22,33
643
+ ], dtype: dtype
644
+ )
645
+ end
646
+
647
+ it "returns main diagonal for square matrix" do
648
+ expect(@square_matrix.diagonal).to eq(NMatrix.new [3], [23,2,32])
649
+ end
650
+
651
+ it "returns main diagonal for rectangular matrix" do
652
+ expect(@rect_matrix.diagonal).to eq(NMatrix.new [3], [23,2,32])
653
+ end
654
+
655
+ it "returns anti-diagonal for square matrix" do
656
+ expect(@square_matrix.diagonal(false)).to eq(NMatrix.new [3], [23,2,33])
657
+ end
658
+
659
+ it "returns anti-diagonal for rectangular matrix" do
660
+ expect(@square_matrix.diagonal(false)).to eq(NMatrix.new [3], [23,2,33])
661
+ end
662
+ end
663
+ end
664
+
665
+ context "#repeat" do
666
+ before do
667
+ @sample_matrix = NMatrix.new([2, 2], [1, 2, 3, 4])
668
+ end
669
+
670
+ it "checks count argument" do
671
+ expect{@sample_matrix.repeat(1, 0)}.to raise_error(ArgumentError)
672
+ expect{@sample_matrix.repeat(-2, 0)}.to raise_error(ArgumentError)
673
+ end
674
+
675
+ it "returns repeated matrix" do
676
+ expect(@sample_matrix.repeat(2, 0)).to eq(NMatrix.new([4, 2], [1, 2, 3, 4, 1, 2, 3, 4]))
677
+ expect(@sample_matrix.repeat(2, 1)).to eq(NMatrix.new([2, 4], [1, 2, 1, 2, 3, 4, 3, 4]))
678
+ end
679
+ end
680
+
681
+ context "#meshgrid" do
682
+ before do
683
+ @x, @y, @z = [1, 2, 3], NMatrix.new([2, 1], [4, 5]), [6, 7]
684
+ @two_dim = NMatrix.new([2, 2], [1, 2, 3, 4])
685
+ @two_dim_array = [[4], [5]]
686
+ @expected_result = [NMatrix.new([2, 3], [1, 2, 3, 1, 2, 3]), NMatrix.new([2, 3], [4, 4, 4, 5, 5, 5])]
687
+ @expected_for_ij = [NMatrix.new([3, 2], [1, 1, 2, 2, 3, 3]), NMatrix.new([3, 2], [4, 5, 4, 5, 4, 5])]
688
+ @expected_for_sparse = [NMatrix.new([1, 3], [1, 2, 3]), NMatrix.new([2, 1], [4, 5])]
689
+ @expected_for_sparse_ij = [NMatrix.new([3, 1], [1, 2, 3]), NMatrix.new([1, 2], [4, 5])]
690
+ @expected_3dim = [NMatrix.new([1, 3, 1], [1, 2, 3]).repeat(2, 0).repeat(2, 2),
691
+ NMatrix.new([2, 1, 1], [4, 5]).repeat(3, 1).repeat(2, 2),
692
+ NMatrix.new([1, 1, 2], [6, 7]).repeat(2, 0).repeat(3, 1)]
693
+ @expected_3dim_sparse_ij = [NMatrix.new([3, 1, 1], [1, 2, 3]),
694
+ NMatrix.new([1, 2, 1], [4, 5]),
695
+ NMatrix.new([1, 1, 2], [6, 7])]
696
+ end
697
+
698
+ it "checks arrays count" do
699
+ expect{NMatrix.meshgrid([@x])}.to raise_error(ArgumentError)
700
+ expect{NMatrix.meshgrid([])}.to raise_error(ArgumentError)
701
+ end
702
+
703
+ it "flattens input arrays before use" do
704
+ expect(NMatrix.meshgrid([@two_dim, @two_dim_array])).to eq(NMatrix.meshgrid([@two_dim.to_flat_array, @two_dim_array.flatten]))
705
+ end
706
+
707
+ it "returns new NMatrixes" do
708
+ expect(NMatrix.meshgrid([@x, @y])).to eq(@expected_result)
709
+ end
710
+
711
+ it "has option :sparse" do
712
+ expect(NMatrix.meshgrid([@x, @y], sparse: true)).to eq(@expected_for_sparse)
713
+ end
714
+
715
+ it "has option :indexing" do
716
+ expect(NMatrix.meshgrid([@x, @y], indexing: :ij)).to eq(@expected_for_ij)
717
+ expect(NMatrix.meshgrid([@x, @y], indexing: :xy)).to eq(@expected_result)
718
+ expect{NMatrix.meshgrid([@x, @y], indexing: :not_ij_not_xy)}.to raise_error(ArgumentError)
719
+ end
720
+
721
+ it "works well with both options set" do
722
+ expect(NMatrix.meshgrid([@x, @y], sparse: true, indexing: :ij)).to eq(@expected_for_sparse_ij)
723
+ end
724
+
725
+ it "is able to take more than two arrays as arguments and works well with options" do
726
+ expect(NMatrix.meshgrid([@x, @y, @z])).to eq(@expected_3dim)
727
+ expect(NMatrix.meshgrid([@x, @y, @z], sparse: true, indexing: :ij)).to eq(@expected_3dim_sparse_ij)
728
+ end
729
+ end
581
730
  end