nmatrix-atlas 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/ext/nmatrix/data/complex.h +364 -0
  3. data/ext/nmatrix/data/data.h +638 -0
  4. data/ext/nmatrix/data/meta.h +64 -0
  5. data/ext/nmatrix/data/ruby_object.h +389 -0
  6. data/ext/nmatrix/math/asum.h +120 -0
  7. data/ext/nmatrix/math/cblas_enums.h +36 -0
  8. data/ext/nmatrix/math/cblas_templates_core.h +507 -0
  9. data/ext/nmatrix/math/gemm.h +241 -0
  10. data/ext/nmatrix/math/gemv.h +178 -0
  11. data/ext/nmatrix/math/getrf.h +255 -0
  12. data/ext/nmatrix/math/getrs.h +121 -0
  13. data/ext/nmatrix/math/imax.h +79 -0
  14. data/ext/nmatrix/math/laswp.h +165 -0
  15. data/ext/nmatrix/math/long_dtype.h +49 -0
  16. data/ext/nmatrix/math/math.h +744 -0
  17. data/ext/nmatrix/math/nrm2.h +160 -0
  18. data/ext/nmatrix/math/rot.h +117 -0
  19. data/ext/nmatrix/math/rotg.h +106 -0
  20. data/ext/nmatrix/math/scal.h +71 -0
  21. data/ext/nmatrix/math/trsm.h +332 -0
  22. data/ext/nmatrix/math/util.h +148 -0
  23. data/ext/nmatrix/nm_memory.h +60 -0
  24. data/ext/nmatrix/nmatrix.h +408 -0
  25. data/ext/nmatrix/ruby_constants.h +106 -0
  26. data/ext/nmatrix/storage/common.h +176 -0
  27. data/ext/nmatrix/storage/dense/dense.h +128 -0
  28. data/ext/nmatrix/storage/list/list.h +137 -0
  29. data/ext/nmatrix/storage/storage.h +98 -0
  30. data/ext/nmatrix/storage/yale/class.h +1139 -0
  31. data/ext/nmatrix/storage/yale/iterators/base.h +142 -0
  32. data/ext/nmatrix/storage/yale/iterators/iterator.h +130 -0
  33. data/ext/nmatrix/storage/yale/iterators/row.h +449 -0
  34. data/ext/nmatrix/storage/yale/iterators/row_stored.h +139 -0
  35. data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +168 -0
  36. data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +123 -0
  37. data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
  38. data/ext/nmatrix/storage/yale/yale.h +202 -0
  39. data/ext/nmatrix/types.h +54 -0
  40. data/ext/nmatrix/util/io.h +115 -0
  41. data/ext/nmatrix/util/sl_list.h +143 -0
  42. data/ext/nmatrix/util/util.h +78 -0
  43. data/ext/nmatrix_atlas/extconf.rb +250 -0
  44. data/ext/nmatrix_atlas/math_atlas.cpp +1206 -0
  45. data/ext/nmatrix_atlas/math_atlas/cblas_templates_atlas.h +72 -0
  46. data/ext/nmatrix_atlas/math_atlas/clapack_templates.h +332 -0
  47. data/ext/nmatrix_atlas/math_atlas/geev.h +82 -0
  48. data/ext/nmatrix_atlas/math_atlas/gesdd.h +83 -0
  49. data/ext/nmatrix_atlas/math_atlas/gesvd.h +81 -0
  50. data/ext/nmatrix_atlas/math_atlas/inc.h +47 -0
  51. data/ext/nmatrix_atlas/nmatrix_atlas.cpp +44 -0
  52. data/lib/nmatrix/atlas.rb +213 -0
  53. data/lib/nmatrix/lapack_ext_common.rb +69 -0
  54. data/spec/00_nmatrix_spec.rb +730 -0
  55. data/spec/01_enum_spec.rb +190 -0
  56. data/spec/02_slice_spec.rb +389 -0
  57. data/spec/03_nmatrix_monkeys_spec.rb +78 -0
  58. data/spec/2x2_dense_double.mat +0 -0
  59. data/spec/4x4_sparse.mat +0 -0
  60. data/spec/4x5_dense.mat +0 -0
  61. data/spec/blas_spec.rb +193 -0
  62. data/spec/elementwise_spec.rb +303 -0
  63. data/spec/homogeneous_spec.rb +99 -0
  64. data/spec/io/fortran_format_spec.rb +88 -0
  65. data/spec/io/harwell_boeing_spec.rb +98 -0
  66. data/spec/io/test.rua +9 -0
  67. data/spec/io_spec.rb +149 -0
  68. data/spec/lapack_core_spec.rb +482 -0
  69. data/spec/leakcheck.rb +16 -0
  70. data/spec/math_spec.rb +730 -0
  71. data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
  72. data/spec/nmatrix_yale_spec.rb +286 -0
  73. data/spec/plugins/atlas/atlas_spec.rb +242 -0
  74. data/spec/rspec_monkeys.rb +56 -0
  75. data/spec/rspec_spec.rb +34 -0
  76. data/spec/shortcuts_spec.rb +310 -0
  77. data/spec/slice_set_spec.rb +157 -0
  78. data/spec/spec_helper.rb +140 -0
  79. data/spec/stat_spec.rb +203 -0
  80. data/spec/test.pcd +20 -0
  81. data/spec/utm5940.mtx +83844 -0
  82. metadata +159 -0
@@ -0,0 +1,16 @@
1
+ require "./lib/nmatrix"
2
+
3
+ # Fixed:
4
+ #n = NMatrix.new(:yale, [8,2], :int64)
5
+ #m = NMatrix.new(:yale, [2,8], :int64)
6
+ #100.times do
7
+ # n.dot(m)
8
+ #end
9
+ #GC.start
10
+
11
+ # Remaining:
12
+ 100.times do |t|
13
+ n = NMatrix.new(:dense, 1000, :float64)
14
+ n[0,t] = 1.0
15
+ puts n[t,0]
16
+ end
@@ -0,0 +1,730 @@
1
+ # = NMatrix
2
+ #
3
+ # A linear algebra library for scientific computation in Ruby.
4
+ # NMatrix is part of SciRuby.
5
+ #
6
+ # NMatrix was originally inspired by and derived from NArray, by
7
+ # Masahiro Tanaka: http://narray.rubyforge.org
8
+ #
9
+ # == Copyright Information
10
+ #
11
+ # SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
12
+ # NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
13
+ #
14
+ # Please see LICENSE.txt for additional copyright notices.
15
+ #
16
+ # == Contributing
17
+ #
18
+ # By contributing source code to SciRuby, you agree to be bound by
19
+ # our Contributor Agreement:
20
+ #
21
+ # * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
22
+ #
23
+ # == math_spec.rb
24
+ #
25
+ # Tests for non-BLAS and non-LAPACK math functions, or for simplified
26
+ # versions of unfriendly BLAS and LAPACK functions.
27
+ #
28
+
29
+ require 'spec_helper'
30
+
31
+ describe "math" do
32
+ context "elementwise math functions" do
33
+
34
+ [:dense,:list,:yale].each do |stype|
35
+ context stype do
36
+
37
+ [:int64,:float64].each do |dtype|
38
+ context dtype do
39
+ before :each do
40
+ @size = [2,2]
41
+ @m = NMatrix.seq(@size, dtype: dtype, stype: stype)+1
42
+ @a = @m.to_a.flatten
43
+ end
44
+
45
+ NMatrix::NMMath::METHODS_ARITY_1.each do |meth|
46
+ #skip inverse regular trig functions
47
+ next if meth.to_s.start_with?('a') and (not meth.to_s.end_with?('h')) \
48
+ and NMatrix::NMMath::METHODS_ARITY_1.include?(
49
+ meth.to_s[1...meth.to_s.length].to_sym)
50
+ next if meth == :atanh
51
+
52
+ if meth == :-@
53
+ it "should correctly apply elementwise negation" do
54
+ expect(@m.send(meth)).to eq N.new(@size, @a.map { |e| -e }, dtype: dtype, stype: stype)
55
+ end
56
+ next
57
+ end
58
+
59
+ it "should correctly apply elementwise #{meth}" do
60
+
61
+ expect(@m.send(meth)).to eq N.new(@size, @a.map{ |e| Math.send(meth, e) },
62
+ dtype: :float64, stype: stype)
63
+ end
64
+ end
65
+
66
+ NMatrix::NMMath::METHODS_ARITY_2.each do |meth|
67
+ next if meth == :atan2
68
+ it "should correctly apply elementwise #{meth}" do
69
+ expect(@m.send(meth, @m)).to eq N.new(@size, @a.map{ |e|
70
+ Math.send(meth, e, e) },
71
+ dtype: :float64,
72
+ stype: stype)
73
+ end
74
+
75
+ it "should correctly apply elementwise #{meth} with a scalar first arg" do
76
+ expect(Math.send(meth, 1, @m)).to eq N.new(@size, @a.map { |e| Math.send(meth, 1, e) }, dtype: :float64, stype: stype)
77
+ end
78
+
79
+ it "should correctly apply elementwise #{meth} with a scalar second arg" do
80
+ expect(@m.send(meth, 1)).to eq N.new(@size, @a.map { |e| Math.send(meth, e, 1) }, dtype: :float64, stype: stype)
81
+ end
82
+ end
83
+
84
+ it "should correctly apply elementwise natural log" do
85
+ expect(@m.log).to eq N.new(@size, [0, Math.log(2), Math.log(3), Math.log(4)],
86
+ dtype: :float64, stype: stype)
87
+ end
88
+
89
+ it "should correctly apply elementwise log with arbitrary base" do
90
+ expect(@m.log(3)).to eq N.new(@size, [0, Math.log(2,3), 1, Math.log(4,3)],
91
+ dtype: :float64, stype: stype)
92
+ end
93
+
94
+ context "inverse trig functions" do
95
+ before :each do
96
+ @m = NMatrix.seq(@size, dtype: dtype, stype: stype)/4
97
+ @a = @m.to_a.flatten
98
+ end
99
+ [:asin, :acos, :atan, :atanh].each do |atf|
100
+
101
+ it "should correctly apply elementwise #{atf}" do
102
+ expect(@m.send(atf)).to eq N.new(@size,
103
+ @a.map{ |e| Math.send(atf, e) },
104
+ dtype: :float64, stype: stype)
105
+ end
106
+ end
107
+
108
+ it "should correctly apply elementtwise atan2" do
109
+ expect(@m.atan2(@m*0+1)).to eq N.new(@size,
110
+ @a.map { |e| Math.send(:atan2, e, 1) }, dtype: :float64, stype: stype)
111
+ end
112
+
113
+ it "should correctly apply elementwise atan2 with a scalar first arg" do
114
+ expect(Math.atan2(1, @m)).to eq N.new(@size, @a.map { |e| Math.send(:atan2, 1, e) }, dtype: :float64, stype: stype)
115
+ end
116
+
117
+ it "should correctly apply elementwise atan2 with a scalar second arg" do
118
+ expect(@m.atan2(1)).to eq N.new(@size, @a.map { |e| Math.send(:atan2, e, 1) }, dtype: :float64, stype: stype)
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ context "Floor and ceil for #{stype}" do
125
+
126
+ [:floor, :ceil].each do |meth|
127
+ ALL_DTYPES.each do |dtype|
128
+ context dtype do
129
+ before :each do
130
+ @size = [2,2]
131
+ @m = NMatrix.seq(@size, dtype: dtype, stype: stype)+1
132
+ @a = @m.to_a.flatten
133
+ end
134
+
135
+ if dtype.to_s.match(/int/) or [:byte, :object].include?(dtype)
136
+ it "should return #{dtype} for #{dtype}" do
137
+
138
+ expect(@m.send(meth)).to eq N.new(@size, @a.map { |e| e.send(meth) }, dtype: dtype, stype: stype)
139
+
140
+ if dtype == :object
141
+ expect(@m.send(meth).dtype).to eq :object
142
+ else
143
+ expect(@m.send(meth).integer_dtype?).to eq true
144
+ end
145
+ end
146
+ elsif dtype.to_s.match(/float/)
147
+ it "should return dtype int64 for #{dtype}" do
148
+
149
+ expect(@m.send(meth)).to eq N.new(@size, @a.map { |e| e.send(meth) }, dtype: dtype, stype: stype)
150
+
151
+ expect(@m.send(meth).dtype).to eq :int64
152
+ end
153
+ elsif dtype.to_s.match(/complex/)
154
+ it "should properly calculate #{meth} for #{dtype}" do
155
+
156
+ expect(@m.send(meth)).to eq N.new(@size, @a.map { |e| e = Complex(e.real.send(meth), e.imag.send(meth)) }, dtype: dtype, stype: stype)
157
+
158
+ expect(@m.send(meth).dtype).to eq :complex64 if dtype == :complex64
159
+ expect(@m.send(meth).dtype).to eq :complex128 if dtype == :complex128
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ context "#round for #{stype}" do
168
+ ALL_DTYPES.each do |dtype|
169
+ context dtype do
170
+ before :each do
171
+ @size = [2,2]
172
+ @mat = NMatrix.new @size, [1.33334, 0.9998, 1.9999, -8.9999],
173
+ dtype: dtype, stype: stype
174
+ @ans = @mat.to_a.flatten
175
+ end
176
+
177
+ it "rounds" do
178
+ expect(@mat.round).to eq(N.new(@size, @ans.map { |a| a.round},
179
+ dtype: dtype, stype: stype))
180
+ end unless(/complex/ =~ dtype)
181
+
182
+ it "rounds with args" do
183
+ expect(@mat.round(2)).to eq(N.new(@size, @ans.map { |a| a.round(2)},
184
+ dtype: dtype, stype: stype))
185
+ end unless(/complex/ =~ dtype)
186
+
187
+ it "rounds complex with args" do
188
+ puts @mat.round(2)
189
+ expect(@mat.round(2)).to be_within(0.0001).of(N.new [2,2], @ans.map {|a|
190
+ Complex(a.real.round(2), a.imag.round(2))},dtype: dtype, stype: stype)
191
+ end if(/complex/ =~ dtype)
192
+
193
+ it "rounds complex" do
194
+ expect(@mat.round).to eq(N.new [2,2], @ans.map {|a|
195
+ Complex(a.real.round, a.imag.round)},dtype: dtype, stype: stype)
196
+ end if(/complex/ =~ dtype)
197
+ end
198
+ end
199
+ end
200
+
201
+ end
202
+ end
203
+ end
204
+
205
+ NON_INTEGER_DTYPES.each do |dtype|
206
+ next if dtype == :object
207
+ context dtype do
208
+ before do
209
+ @m = NMatrix.new([3,4], GETRF_EXAMPLE_ARRAY, dtype: dtype)
210
+ @err = case dtype
211
+ when :float32, :complex64
212
+ 1e-6
213
+ when :float64, :complex128
214
+ 1e-14
215
+ end
216
+ end
217
+
218
+ #haven't check this spec yet. Also it doesn't check all the elements of the matrix.
219
+ it "should correctly factorize a matrix" do
220
+ a = @m.factorize_lu
221
+ expect(a).to be_within(@err).of(NMatrix.new([3,4], GETRF_SOLUTION_ARRAY, dtype: dtype))
222
+ end
223
+
224
+ it "also returns the permutation matrix" do
225
+ a, p = @m.factorize_lu perm_matrix: true
226
+
227
+ expect(a).to be_within(@err).of(NMatrix.new([3,4], GETRF_SOLUTION_ARRAY, dtype: dtype))
228
+
229
+ p_true = NMatrix.new([3,3], [0,0,1,1,0,0,0,1,0], dtype: dtype)
230
+ expect(p).to eq(p_true)
231
+ end
232
+ end
233
+ end
234
+
235
+ NON_INTEGER_DTYPES.each do |dtype|
236
+ next if dtype == :object
237
+ context dtype do
238
+
239
+ it "calculates cholesky decomposition using potrf (lower)" do
240
+ #a = NMatrix.new([3,3],[1,1,1, 1,2,2, 1,2,6], dtype: dtype)
241
+ # We use the matrix
242
+ # 1 1 1
243
+ # 1 2 2
244
+ # 1 2 6
245
+ # which is symmetric and positive-definite as required, but
246
+ # we need only store the lower-half of the matrix.
247
+ a = NMatrix.new([3,3],[1,0,0, 1,2,0, 1,2,6], dtype: dtype)
248
+ begin
249
+ r = a.potrf!(:lower)
250
+
251
+ b = NMatrix.new([3,3],[1,0,0, 1,1,0, 1,1,2], dtype: dtype)
252
+ expect(a).to eq(b)
253
+ expect(r).to eq(b)
254
+ rescue NotImplementedError
255
+ pending "potrf! not implemented without plugins"
256
+ end
257
+ end
258
+
259
+ it "calculates cholesky decomposition using potrf (upper)" do
260
+ a = NMatrix.new([3,3],[1,1,1, 0,2,2, 0,0,6], dtype: dtype)
261
+ begin
262
+ r = a.potrf!(:upper)
263
+
264
+ b = NMatrix.new([3,3],[1,1,1, 0,1,1, 0,0,2], dtype: dtype)
265
+ expect(a).to eq(b)
266
+ expect(r).to eq(b)
267
+ rescue NotImplementedError
268
+ pending "potrf! not implemented without plugins"
269
+ end
270
+ end
271
+
272
+ it "calculates cholesky decomposition using #factorize_cholesky" do
273
+ a = NMatrix.new([3,3],[1,2,1, 2,13,5, 1,5,6], dtype: dtype)
274
+ begin
275
+ u,l = a.factorize_cholesky
276
+
277
+ l_true = NMatrix.new([3,3],[1,0,0, 2,3,0, 1,1,2], dtype: dtype)
278
+ u_true = l_true.transpose
279
+ expect(u).to eq(u_true)
280
+ expect(l).to eq(l_true)
281
+ rescue NotImplementedError
282
+ pending "potrf! not implemented without plugins"
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ ALL_DTYPES.each do |dtype|
289
+ next if dtype == :byte #doesn't work for unsigned types
290
+ next if dtype == :object
291
+
292
+ context dtype do
293
+ err = case dtype
294
+ when :float32, :complex64
295
+ 1e-4
296
+ else #integer matrices will return :float64
297
+ 1e-13
298
+ end
299
+
300
+ it "should correctly invert a matrix in place (bang)" do
301
+ a = NMatrix.new(:dense, 5, [1, 8,-9, 7, 5,
302
+ 0, 1, 0, 4, 4,
303
+ 0, 0, 1, 2, 5,
304
+ 0, 0, 0, 1,-5,
305
+ 0, 0, 0, 0, 1 ], dtype)
306
+ b = NMatrix.new(:dense, 5, [1,-8, 9, 7, 17,
307
+ 0, 1, 0,-4,-24,
308
+ 0, 0, 1,-2,-15,
309
+ 0, 0, 0, 1, 5,
310
+ 0, 0, 0, 0, 1,], dtype)
311
+ if a.integer_dtype?
312
+ expect{a.invert!}.to raise_error(DataTypeError)
313
+ else
314
+ #should return inverse as well as modifying a
315
+ r = a.invert!
316
+ expect(a).to be_within(err).of(b)
317
+ expect(r).to be_within(err).of(b)
318
+ end
319
+ end
320
+
321
+ it "should correctly invert a matrix out-of-place" do
322
+ a = NMatrix.new(:dense, 3, [1,2,3,0,1,4,5,6,0], dtype)
323
+
324
+ if a.integer_dtype?
325
+ b = NMatrix.new(:dense, 3, [-24,18,5,20,-15,-4,-5,4,1], :float64)
326
+ else
327
+ b = NMatrix.new(:dense, 3, [-24,18,5,20,-15,-4,-5,4,1], dtype)
328
+ end
329
+
330
+ expect(a.invert).to be_within(err).of(b)
331
+ end
332
+ end
333
+ end
334
+
335
+ # TODO: Get it working with ROBJ too
336
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |left_dtype|
337
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |right_dtype|
338
+
339
+ # Won't work if they're both 1-byte, due to overflow.
340
+ next if [:byte,:int8].include?(left_dtype) && [:byte,:int8].include?(right_dtype)
341
+
342
+ # For now, don't bother testing int-int mult.
343
+ #next if [:int8,:int16,:int32,:int64].include?(left_dtype) && [:int8,:int16,:int32,:int64].include?(right_dtype)
344
+ it "dense handles #{left_dtype.to_s} dot #{right_dtype.to_s} matrix multiplication" do
345
+ #STDERR.puts "dtype=#{dtype.to_s}"
346
+ #STDERR.puts "2"
347
+
348
+ nary = if left_dtype.to_s =~ /complex/
349
+ COMPLEX_MATRIX43A_ARRAY
350
+ else
351
+ MATRIX43A_ARRAY
352
+ end
353
+
354
+ mary = if right_dtype.to_s =~ /complex/
355
+ COMPLEX_MATRIX32A_ARRAY
356
+ else
357
+ MATRIX32A_ARRAY
358
+ end
359
+
360
+ n = NMatrix.new([4,3], nary, dtype: left_dtype, stype: :dense)
361
+ m = NMatrix.new([3,2], mary, dtype: right_dtype, stype: :dense)
362
+
363
+ expect(m.shape[0]).to eq(3)
364
+ expect(m.shape[1]).to eq(2)
365
+ expect(m.dim).to eq(2)
366
+
367
+ expect(n.shape[0]).to eq(4)
368
+ expect(n.shape[1]).to eq(3)
369
+ expect(n.dim).to eq(2)
370
+
371
+ expect(n.shape[1]).to eq(m.shape[0])
372
+
373
+ r = n.dot m
374
+
375
+ expect(r[0,0]).to eq(273.0)
376
+ expect(r[0,1]).to eq(455.0)
377
+ expect(r[1,0]).to eq(243.0)
378
+ expect(r[1,1]).to eq(235.0)
379
+ expect(r[2,0]).to eq(244.0)
380
+ expect(r[2,1]).to eq(205.0)
381
+ expect(r[3,0]).to eq(102.0)
382
+ expect(r[3,1]).to eq(160.0)
383
+
384
+ #r.dtype.should == :float64 unless left_dtype == :float32 && right_dtype == :float32
385
+ end
386
+ end
387
+ end
388
+
389
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |left_dtype|
390
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |right_dtype|
391
+
392
+ # Won't work if they're both 1-byte, due to overflow.
393
+ next if [:byte,:int8].include?(left_dtype) && [:byte,:int8].include?(right_dtype)
394
+
395
+ it "dense handles #{left_dtype.to_s} dot #{right_dtype.to_s} vector multiplication" do
396
+ #STDERR.puts "dtype=#{dtype.to_s}"
397
+ #STDERR.puts "2"
398
+ n = NMatrix.new([4,3], [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0], dtype: left_dtype)
399
+
400
+ m = NMatrix.new([3,1], [2.0, 1.0, 0.0], dtype: right_dtype)
401
+
402
+ expect(m.shape[0]).to eq(3)
403
+ expect(m.shape[1]).to eq(1)
404
+
405
+ expect(n.shape[0]).to eq(4)
406
+ expect(n.shape[1]).to eq(3)
407
+ expect(n.dim).to eq(2)
408
+
409
+ expect(n.shape[1]).to eq(m.shape[0])
410
+
411
+ r = n.dot m
412
+ # r.class.should == NVector
413
+
414
+ expect(r[0,0]).to eq(4)
415
+ expect(r[1,0]).to eq(13)
416
+ expect(r[2,0]).to eq(22)
417
+ expect(r[3,0]).to eq(31)
418
+
419
+ #r.dtype.should == :float64 unless left_dtype == :float32 && right_dtype == :float32
420
+ end
421
+ end
422
+ end
423
+
424
+ ALL_DTYPES.each do |dtype|
425
+ next if integer_dtype?(dtype)
426
+ context "#cov dtype #{dtype}" do
427
+ before do
428
+ @n = NMatrix.new( [5,3], [4.0,2.0,0.60,
429
+ 4.2,2.1,0.59,
430
+ 3.9,2.0,0.58,
431
+ 4.3,2.1,0.62,
432
+ 4.1,2.2,0.63], dtype: dtype)
433
+ end
434
+
435
+ it "calculates variance co-variance matrix (sample)" do
436
+ expect(@n.cov).to be_within(0.0001).of(NMatrix.new([3,3],
437
+ [0.025 , 0.0075, 0.00175,
438
+ 0.0075, 0.007 , 0.00135,
439
+ 0.00175, 0.00135 , 0.00043 ], dtype: dtype)
440
+ )
441
+ end
442
+
443
+ it "calculates variance co-variance matrix (population)" do
444
+ expect(@n.cov(for_sample_data: false)).to be_within(0.0001).of(NMatrix.new([3,3],
445
+ [2.0000e-02, 6.0000e-03, 1.4000e-03,
446
+ 6.0000e-03, 5.6000e-03, 1.0800e-03,
447
+ 1.4000e-03, 1.0800e-03, 3.4400e-04], dtype: dtype)
448
+ )
449
+ end
450
+ end
451
+
452
+ context "#corr #{dtype}" do
453
+ it "calculates the correlation matrix" do
454
+ n = NMatrix.new([5,3], [4.0,2.0,0.60,
455
+ 4.2,2.1,0.59,
456
+ 3.9,2.0,0.58,
457
+ 4.3,2.1,0.62,
458
+ 4.1,2.2,0.63], dtype: dtype)
459
+ expect(n.corr).to be_within(0.001).of(NMatrix.new([3,3],
460
+ [1.00000, 0.56695, 0.53374,
461
+ 0.56695, 1.00000, 0.77813,
462
+ 0.53374, 0.77813, 1.00000], dtype: dtype))
463
+ end unless dtype =~ /complex/
464
+ end
465
+
466
+ context "#symmetric? for #{dtype}" do
467
+ it "should return true for symmetric matrix" do
468
+ n = NMatrix.new([3,3], [1.00000, 0.56695, 0.53374,
469
+ 0.56695, 1.00000, 0.77813,
470
+ 0.53374, 0.77813, 1.00000], dtype: dtype)
471
+ expect(n.symmetric?).to be_truthy
472
+ end
473
+ end
474
+
475
+ context "#hermitian? for #{dtype}" do
476
+ it "should return true for complex hermitian or non-complex symmetric matrix" do
477
+ n = NMatrix.new([3,3], [1.00000, 0.56695, 0.53374,
478
+ 0.56695, 1.00000, 0.77813,
479
+ 0.53374, 0.77813, 1.00000], dtype: dtype) unless dtype =~ /complex/
480
+ n = NMatrix.new([3,3], [1.1, Complex(1.2,1.3), Complex(1.4,1.5),
481
+ Complex(1.2,-1.3), 1.9, Complex(1.8,1.7),
482
+ Complex(1.4,-1.5), Complex(1.8,-1.7), 1.3], dtype: dtype) if dtype =~ /complex/
483
+ expect(n.hermitian?).to be_truthy
484
+ end
485
+ end
486
+
487
+ context "#permute_columns for #{dtype}" do
488
+ it "check that #permute_columns works correctly by considering every premutation of a 3x3 matrix" do
489
+ n = NMatrix.new([3,3], [1,0,0,
490
+ 0,2,0,
491
+ 0,0,3], dtype: dtype)
492
+ expect(n.permute_columns([0,1,2], {convention: :intuitive})).to eq(NMatrix.new([3,3], [1,0,0,
493
+ 0,2,0,
494
+ 0,0,3], dtype: dtype))
495
+ expect(n.permute_columns([0,2,1], {convention: :intuitive})).to eq(NMatrix.new([3,3], [1,0,0,
496
+ 0,0,2,
497
+ 0,3,0], dtype: dtype))
498
+ expect(n.permute_columns([1,0,2], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,1,0,
499
+ 2,0,0,
500
+ 0,0,3], dtype: dtype))
501
+ expect(n.permute_columns([1,2,0], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,0,1,
502
+ 2,0,0,
503
+ 0,3,0], dtype: dtype))
504
+ expect(n.permute_columns([2,0,1], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,1,0,
505
+ 0,0,2,
506
+ 3,0,0], dtype: dtype))
507
+ expect(n.permute_columns([2,1,0], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,0,1,
508
+ 0,2,0,
509
+ 3,0,0], dtype: dtype))
510
+ expect(n.permute_columns([0,1,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [1,0,0,
511
+ 0,2,0,
512
+ 0,0,3], dtype: dtype))
513
+ expect(n.permute_columns([0,2,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [1,0,0,
514
+ 0,0,2,
515
+ 0,3,0], dtype: dtype))
516
+ expect(n.permute_columns([1,1,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,1,0,
517
+ 2,0,0,
518
+ 0,0,3], dtype: dtype))
519
+ expect(n.permute_columns([1,2,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,0,1,
520
+ 2,0,0,
521
+ 0,3,0], dtype: dtype))
522
+ expect(n.permute_columns([2,2,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,1,0,
523
+ 0,0,2,
524
+ 3,0,0], dtype: dtype))
525
+ expect(n.permute_columns([2,1,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,0,1,
526
+ 0,2,0,
527
+ 3,0,0], dtype: dtype))
528
+ end
529
+ it "additional tests for #permute_columns with convention :intuitive" do
530
+ m = NMatrix.new([1,4], [0,1,2,3], dtype: dtype)
531
+ perm = [1,0,3,2]
532
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,4], perm, dtype: dtype))
533
+
534
+ m = NMatrix.new([1,5], [0,1,2,3,4], dtype: dtype)
535
+ perm = [1,0,4,3,2]
536
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,5], perm, dtype: dtype))
537
+
538
+ m = NMatrix.new([1,6], [0,1,2,3,4,5], dtype: dtype)
539
+ perm = [2,4,1,0,5,3]
540
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,6], perm, dtype: dtype))
541
+
542
+ m = NMatrix.new([1,7], [0,1,2,3,4,5,6], dtype: dtype)
543
+ perm = [1,3,5,6,0,2,4]
544
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,7], perm, dtype: dtype))
545
+
546
+ m = NMatrix.new([1,8], [0,1,2,3,4,5,6,7], dtype: dtype)
547
+ perm = [6,7,5,4,1,3,0,2]
548
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,8], perm, dtype: dtype))
549
+ end
550
+ end
551
+ end
552
+
553
+ context "#solve" do
554
+ NON_INTEGER_DTYPES.each do |dtype|
555
+ next if dtype == :object # LU factorization doesnt work for :object yet
556
+ it "solves linear equation for dtype #{dtype}" do
557
+ a = NMatrix.new [2,2], [3,1,1,2], dtype: dtype
558
+ b = NMatrix.new [2,1], [9,8], dtype: dtype
559
+
560
+ expect(a.solve(b)).to eq(NMatrix.new [2,1], [2,3], dtype: dtype)
561
+ end
562
+
563
+ it "solves linear equation for #{dtype} (non-symmetric matrix)" do
564
+ a = NMatrix.new [3,3], [1,1,1, -1,0,1, 3,4,6], dtype: dtype
565
+ b = NMatrix.new [3,1], [6,2,29], dtype: dtype
566
+
567
+ err = case dtype
568
+ when :float32, :complex64
569
+ 1e-5
570
+ else
571
+ 1e-14
572
+ end
573
+
574
+ expect(a.solve(b)).to be_within(err).of(NMatrix.new([3,1], [1,2,3], dtype: dtype))
575
+ end
576
+
577
+ it "solves linear equation for dtype #{dtype} (non-vector rhs)" do
578
+ a = NMatrix.new [3,3], [1,0,0, -1,0,1, 2,1,1], dtype: dtype
579
+ b = NMatrix.new [3,2], [1,0, 1,2, 4,2], dtype: dtype
580
+
581
+ expect(a.solve(b)).to eq(NMatrix.new [3,2], [1,0, 0,0, 2,2], dtype: dtype)
582
+ end
583
+ end
584
+ end
585
+
586
+ context "#hessenberg" do
587
+ FLOAT_DTYPES.each do |dtype|
588
+ context dtype do
589
+ before do
590
+ @n = NMatrix.new [5,5],
591
+ [0, 2, 0, 1, 1,
592
+ 2, 2, 3, 2, 2,
593
+ 4,-3, 0, 1, 3,
594
+ 6, 1,-6,-5, 4,
595
+ 5, 6, 4, 1, 5], dtype: dtype
596
+ end
597
+
598
+ it "transforms a matrix to Hessenberg form" do
599
+ expect(@n.hessenberg).to be_within(0.0001).of(NMatrix.new([5,5],
600
+ [0.00000,-1.66667, 0.79432,-0.45191,-1.54501,
601
+ -9.00000, 2.95062,-6.89312, 3.22250,-0.19012,
602
+ 0.00000,-8.21682,-0.57379, 5.26966,-1.69976,
603
+ 0.00000, 0.00000,-3.74630,-0.80893, 3.99708,
604
+ 0.00000, 0.00000, 0.00000, 0.04102, 0.43211], dtype: dtype))
605
+ end
606
+ end
607
+ end
608
+ end
609
+
610
+ ALL_DTYPES.each do |dtype|
611
+ [:dense, :yale].each do |stype|
612
+ answer_dtype = integer_dtype?(dtype) ? :int64 : dtype
613
+ next if dtype == :byte
614
+
615
+ context "#pow #{dtype} #{stype}" do
616
+ before do
617
+ @n = NMatrix.new [4,4], [0, 2, 0, 1,
618
+ 2, 2, 3, 2,
619
+ 4,-3, 0, 1,
620
+ 6, 1,-6,-5], dtype: dtype, stype: stype
621
+ end
622
+
623
+ it "raises a square matrix to even power" do
624
+ expect(@n.pow(4)).to eq(NMatrix.new([4,4], [292, 28,-63, -42,
625
+ 360, 96, 51, -14,
626
+ 448,-231,-24,-87,
627
+ -1168, 595,234, 523],
628
+ dtype: answer_dtype,
629
+ stype: stype))
630
+ end
631
+
632
+ it "raises a square matrix to odd power" do
633
+ expect(@n.pow(9)).to eq(NMatrix.new([4,4],[-275128, 279917, 176127, 237451,
634
+ -260104, 394759, 166893, 296081,
635
+ -704824, 285700, 186411, 262002,
636
+ 3209256,-1070870,-918741,-1318584],
637
+ dtype: answer_dtype, stype: stype))
638
+ end
639
+
640
+ it "raises a sqaure matrix to negative power" do
641
+ expect(@n.pow(-3)).to be_within(0.00001).of (NMatrix.new([4,4],
642
+ [1.0647e-02, 4.2239e-04,-6.2281e-05, 2.7680e-03,
643
+ -1.6415e-02, 2.1296e-02, 1.0718e-02, 4.8589e-03,
644
+ 8.6956e-03,-8.6569e-03, 2.8993e-02, 7.2015e-03,
645
+ 5.0034e-02,-1.7500e-02,-3.6777e-02,-1.2128e-02], dtype: answer_dtype,
646
+ stype: stype))
647
+ end unless stype =~ /yale/ or dtype == :object or ALL_DTYPES.grep(/int/).include? dtype
648
+
649
+ it "raises a square matrix to zero" do
650
+ expect(@n.pow(0)).to eq(NMatrix.eye([4,4], dtype: answer_dtype,
651
+ stype: stype))
652
+ end
653
+
654
+ it "raises a square matrix to one" do
655
+ expect(@n.pow(1)).to eq(@n)
656
+ end
657
+ end
658
+ end
659
+ end
660
+
661
+ ALL_DTYPES.each do |dtype|
662
+ [:dense, :yale].each do |stype|
663
+ context "#kron_prod #{dtype} #{stype}" do
664
+ before do
665
+ @a = NMatrix.new([2,2], [1,2,
666
+ 3,4], dtype: dtype, stype: stype)
667
+ @b = NMatrix.new([2,3], [1,1,1,
668
+ 1,1,1], dtype: dtype, stype: stype)
669
+ @c = NMatrix.new([4,6], [1, 1, 1, 2, 2, 2,
670
+ 1, 1, 1, 2, 2, 2,
671
+ 3, 3, 3, 4, 4, 4,
672
+ 3, 3, 3, 4, 4, 4], dtype: dtype, stype: stype)
673
+ end
674
+ it "Compute the Kronecker product of two NMatrix" do
675
+ expect(@a.kron_prod(@b)).to eq(@c)
676
+ end
677
+ end
678
+ end
679
+ end
680
+
681
+ context "determinants" do
682
+ ALL_DTYPES.each do |dtype|
683
+ next if dtype == :object
684
+ context dtype do
685
+ before do
686
+ @a = NMatrix.new([2,2], [1,2,
687
+ 3,4], dtype: dtype)
688
+ @b = NMatrix.new([3,3], [1,2,3,
689
+ 5,0,1,
690
+ 4,1,3], dtype: dtype)
691
+ @c = NMatrix.new([4,4], [1, 0, 1, 1,
692
+ 1, 2, 3, 1,
693
+ 3, 3, 3, 1,
694
+ 1, 2, 3, 4], dtype: dtype)
695
+ @err = case dtype
696
+ when :float32, :complex64
697
+ 1e-6
698
+ when :float64, :complex128
699
+ 1e-14
700
+ else
701
+ 1e-64 # FIXME: should be 0, but be_within(0) does not work.
702
+ end
703
+ end
704
+ it "computes the determinant of 2x2 matrix" do
705
+ expect(@a.det).to be_within(@err).of(-2)
706
+ end
707
+ it "computes the determinant of 3x3 matrix" do
708
+ expect(@b.det).to be_within(@err).of(-8)
709
+ end
710
+ it "computes the determinant of 4x4 matrix" do
711
+ expect(@c.det).to be_within(@err).of(-18)
712
+ end
713
+ it "computes the exact determinant of 2x2 matrix" do
714
+ if dtype == :byte
715
+ expect{@a.det_exact}.to raise_error(DataTypeError)
716
+ else
717
+ expect(@a.det_exact).to be_within(@err).of(-2)
718
+ end
719
+ end
720
+ it "computes the exact determinant of 3x3 matrix" do
721
+ if dtype == :byte
722
+ expect{@a.det_exact}.to raise_error(DataTypeError)
723
+ else
724
+ expect(@b.det_exact).to be_within(@err).of(-8)
725
+ end
726
+ end
727
+ end
728
+ end
729
+ end
730
+ end