nmatrix 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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