nmatrix-fftw 0.2.1

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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/ext/nmatrix/data/complex.h +388 -0
  3. data/ext/nmatrix/data/data.h +652 -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 +745 -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 +438 -0
  25. data/ext/nmatrix/ruby_constants.h +106 -0
  26. data/ext/nmatrix/storage/common.h +177 -0
  27. data/ext/nmatrix/storage/dense/dense.h +129 -0
  28. data/ext/nmatrix/storage/list/list.h +138 -0
  29. data/ext/nmatrix/storage/storage.h +99 -0
  30. data/ext/nmatrix/storage/yale/class.h +1139 -0
  31. data/ext/nmatrix/storage/yale/iterators/base.h +143 -0
  32. data/ext/nmatrix/storage/yale/iterators/iterator.h +131 -0
  33. data/ext/nmatrix/storage/yale/iterators/row.h +450 -0
  34. data/ext/nmatrix/storage/yale/iterators/row_stored.h +140 -0
  35. data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +169 -0
  36. data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +124 -0
  37. data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
  38. data/ext/nmatrix/storage/yale/yale.h +203 -0
  39. data/ext/nmatrix/types.h +55 -0
  40. data/ext/nmatrix/util/io.h +115 -0
  41. data/ext/nmatrix/util/sl_list.h +144 -0
  42. data/ext/nmatrix/util/util.h +78 -0
  43. data/ext/nmatrix_fftw/extconf.rb +122 -0
  44. data/ext/nmatrix_fftw/nmatrix_fftw.cpp +274 -0
  45. data/lib/nmatrix/fftw.rb +343 -0
  46. data/spec/00_nmatrix_spec.rb +736 -0
  47. data/spec/01_enum_spec.rb +190 -0
  48. data/spec/02_slice_spec.rb +389 -0
  49. data/spec/03_nmatrix_monkeys_spec.rb +78 -0
  50. data/spec/2x2_dense_double.mat +0 -0
  51. data/spec/4x4_sparse.mat +0 -0
  52. data/spec/4x5_dense.mat +0 -0
  53. data/spec/blas_spec.rb +193 -0
  54. data/spec/elementwise_spec.rb +303 -0
  55. data/spec/homogeneous_spec.rb +99 -0
  56. data/spec/io/fortran_format_spec.rb +88 -0
  57. data/spec/io/harwell_boeing_spec.rb +98 -0
  58. data/spec/io/test.rua +9 -0
  59. data/spec/io_spec.rb +149 -0
  60. data/spec/lapack_core_spec.rb +482 -0
  61. data/spec/leakcheck.rb +16 -0
  62. data/spec/math_spec.rb +807 -0
  63. data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
  64. data/spec/nmatrix_yale_spec.rb +286 -0
  65. data/spec/plugins/fftw/fftw_spec.rb +348 -0
  66. data/spec/rspec_monkeys.rb +56 -0
  67. data/spec/rspec_spec.rb +34 -0
  68. data/spec/shortcuts_spec.rb +310 -0
  69. data/spec/slice_set_spec.rb +157 -0
  70. data/spec/spec_helper.rb +149 -0
  71. data/spec/stat_spec.rb +203 -0
  72. data/spec/test.pcd +20 -0
  73. data/spec/utm5940.mtx +83844 -0
  74. metadata +151 -0
data/spec/leakcheck.rb ADDED
@@ -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
data/spec/math_spec.rb ADDED
@@ -0,0 +1,807 @@
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
+
557
+ it "solves linear equation for dtype #{dtype}" do
558
+ a = NMatrix.new [2,2], [3,1,1,2], dtype: dtype
559
+ b = NMatrix.new [2,1], [9,8], dtype: dtype
560
+
561
+ expect(a.solve(b)).to eq(NMatrix.new [2,1], [2,3], dtype: dtype)
562
+ end
563
+
564
+ it "solves linear equation for #{dtype} (non-symmetric matrix)" do
565
+ a = NMatrix.new [3,3], [1,1,1, -1,0,1, 3,4,6], dtype: dtype
566
+ b = NMatrix.new [3,1], [6,2,29], dtype: dtype
567
+
568
+ err = case dtype
569
+ when :float32, :complex64
570
+ 1e-5
571
+ else
572
+ 1e-14
573
+ end
574
+
575
+ expect(a.solve(b)).to be_within(err).of(NMatrix.new([3,1], [1,2,3], dtype: dtype))
576
+ end
577
+
578
+ it "solves linear equation for dtype #{dtype} (non-vector rhs)" do
579
+ a = NMatrix.new [3,3], [1,0,0, -1,0,1, 2,1,1], dtype: dtype
580
+ b = NMatrix.new [3,2], [1,0, 1,2, 4,2], dtype: dtype
581
+
582
+ expect(a.solve(b)).to eq(NMatrix.new [3,2], [1,0, 0,0, 2,2], dtype: dtype)
583
+ end
584
+ end
585
+
586
+ FLOAT_DTYPES.each do |dtype|
587
+ context "when form: :lower_tri" do
588
+ let(:a) { NMatrix.new([3,3], [1, 0, 0, 2, 0.5, 0, 3, 3, 9], dtype: dtype) }
589
+
590
+ it "solves a lower triangular linear system A * x = b with vector b" do
591
+ b = NMatrix.new([3,1], [1,2,3], dtype: dtype)
592
+ x = a.solve(b, form: :lower_tri)
593
+ r = a.dot(x) - b
594
+ expect(r.abs.max).to be_within(1e-6).of(0.0)
595
+ end
596
+
597
+ it "solves a lower triangular linear system A * X = B with narrow B" do
598
+ b = NMatrix.new([3,2], [1,2,3,4,5,6], dtype: dtype)
599
+ x = a.solve(b, form: :lower_tri)
600
+ r = (a.dot(x) - b).abs.to_flat_a
601
+ expect(r.max).to be_within(1e-6).of(0.0)
602
+ end
603
+
604
+ it "solves a lower triangular linear system A * X = B with wide B" do
605
+ b = NMatrix.new([3,5], (1..15).to_a, dtype: dtype)
606
+ x = a.solve(b, form: :lower_tri)
607
+ r = (a.dot(x) - b).abs.to_flat_a
608
+ expect(r.max).to be_within(1e-6).of(0.0)
609
+ end
610
+ end
611
+
612
+ context "when form: :upper_tri" do
613
+ let(:a) { NMatrix.new([3,3], [3, 2, 1, 0, 2, 0.5, 0, 0, 9], dtype: dtype) }
614
+
615
+ it "solves an upper triangular linear system A * x = b with vector b" do
616
+ b = NMatrix.new([3,1], [1,2,3], dtype: dtype)
617
+ x = a.solve(b, form: :upper_tri)
618
+ r = a.dot(x) - b
619
+ expect(r.abs.max).to be_within(1e-6).of(0.0)
620
+ end
621
+
622
+ it "solves an upper triangular linear system A * X = B with narrow B" do
623
+ b = NMatrix.new([3,2], [1,2,3,4,5,6], dtype: dtype)
624
+ x = a.solve(b, form: :upper_tri)
625
+ r = (a.dot(x) - b).abs.to_flat_a
626
+ expect(r.max).to be_within(1e-6).of(0.0)
627
+ end
628
+
629
+ it "solves an upper triangular linear system A * X = B with a wide B" do
630
+ b = NMatrix.new([3,5], (1..15).to_a, dtype: dtype)
631
+ x = a.solve(b, form: :upper_tri)
632
+ r = (a.dot(x) - b).abs.to_flat_a
633
+ expect(r.max).to be_within(1e-6).of(0.0)
634
+ end
635
+ end
636
+
637
+ context "when form: :pos_def" do
638
+ let(:a) { NMatrix.new([3,3], [4, 1, 2, 1, 5, 3, 2, 3, 6], dtype: dtype) }
639
+
640
+ it "solves a linear system A * X = b with positive definite A and vector b" do
641
+ b = NMatrix.new([3,1], [6,4,8], dtype: dtype)
642
+ begin
643
+ x = a.solve(b, form: :pos_def)
644
+ expect(x).to be_within(1e-6).of(NMatrix.new([3,1], [1,0,1], dtype: dtype))
645
+ rescue NotImplementedError
646
+ "Suppressing a NotImplementedError when the lapacke or atlas plugin is not available"
647
+ end
648
+ end
649
+
650
+ it "solves a linear system A * X = B with positive definite A and matrix B" do
651
+ b = NMatrix.new([3,2], [8,3,14,13,14,19], dtype: dtype)
652
+ begin
653
+ x = a.solve(b, form: :pos_def)
654
+ expect(x).to be_within(1e-6).of(NMatrix.new([3,2], [1,-1,2,1,1,3], dtype: dtype))
655
+ rescue NotImplementedError
656
+ "Suppressing a NotImplementedError when the lapacke or atlas plugin is not available"
657
+ end
658
+ end
659
+ end
660
+ end
661
+ end
662
+
663
+ context "#hessenberg" do
664
+ FLOAT_DTYPES.each do |dtype|
665
+ context dtype do
666
+ before do
667
+ @n = NMatrix.new [5,5],
668
+ [0, 2, 0, 1, 1,
669
+ 2, 2, 3, 2, 2,
670
+ 4,-3, 0, 1, 3,
671
+ 6, 1,-6,-5, 4,
672
+ 5, 6, 4, 1, 5], dtype: dtype
673
+ end
674
+
675
+ it "transforms a matrix to Hessenberg form" do
676
+ expect(@n.hessenberg).to be_within(0.0001).of(NMatrix.new([5,5],
677
+ [0.00000,-1.66667, 0.79432,-0.45191,-1.54501,
678
+ -9.00000, 2.95062,-6.89312, 3.22250,-0.19012,
679
+ 0.00000,-8.21682,-0.57379, 5.26966,-1.69976,
680
+ 0.00000, 0.00000,-3.74630,-0.80893, 3.99708,
681
+ 0.00000, 0.00000, 0.00000, 0.04102, 0.43211], dtype: dtype))
682
+ end
683
+ end
684
+ end
685
+ end
686
+
687
+ ALL_DTYPES.each do |dtype|
688
+ [:dense, :yale].each do |stype|
689
+ answer_dtype = integer_dtype?(dtype) ? :int64 : dtype
690
+ next if dtype == :byte
691
+
692
+ context "#pow #{dtype} #{stype}" do
693
+ before do
694
+ @n = NMatrix.new [4,4], [0, 2, 0, 1,
695
+ 2, 2, 3, 2,
696
+ 4,-3, 0, 1,
697
+ 6, 1,-6,-5], dtype: dtype, stype: stype
698
+ end
699
+
700
+ it "raises a square matrix to even power" do
701
+ expect(@n.pow(4)).to eq(NMatrix.new([4,4], [292, 28,-63, -42,
702
+ 360, 96, 51, -14,
703
+ 448,-231,-24,-87,
704
+ -1168, 595,234, 523],
705
+ dtype: answer_dtype,
706
+ stype: stype))
707
+ end
708
+
709
+ it "raises a square matrix to odd power" do
710
+ expect(@n.pow(9)).to eq(NMatrix.new([4,4],[-275128, 279917, 176127, 237451,
711
+ -260104, 394759, 166893, 296081,
712
+ -704824, 285700, 186411, 262002,
713
+ 3209256,-1070870,-918741,-1318584],
714
+ dtype: answer_dtype, stype: stype))
715
+ end
716
+
717
+ it "raises a sqaure matrix to negative power" do
718
+ expect(@n.pow(-3)).to be_within(0.00001).of (NMatrix.new([4,4],
719
+ [1.0647e-02, 4.2239e-04,-6.2281e-05, 2.7680e-03,
720
+ -1.6415e-02, 2.1296e-02, 1.0718e-02, 4.8589e-03,
721
+ 8.6956e-03,-8.6569e-03, 2.8993e-02, 7.2015e-03,
722
+ 5.0034e-02,-1.7500e-02,-3.6777e-02,-1.2128e-02], dtype: answer_dtype,
723
+ stype: stype))
724
+ end unless stype =~ /yale/ or dtype == :object or ALL_DTYPES.grep(/int/).include? dtype
725
+
726
+ it "raises a square matrix to zero" do
727
+ expect(@n.pow(0)).to eq(NMatrix.eye([4,4], dtype: answer_dtype,
728
+ stype: stype))
729
+ end
730
+
731
+ it "raises a square matrix to one" do
732
+ expect(@n.pow(1)).to eq(@n)
733
+ end
734
+ end
735
+ end
736
+ end
737
+
738
+ ALL_DTYPES.each do |dtype|
739
+ [:dense, :yale].each do |stype|
740
+ context "#kron_prod #{dtype} #{stype}" do
741
+ before do
742
+ @a = NMatrix.new([2,2], [1,2,
743
+ 3,4], dtype: dtype, stype: stype)
744
+ @b = NMatrix.new([2,3], [1,1,1,
745
+ 1,1,1], dtype: dtype, stype: stype)
746
+ @c = NMatrix.new([4,6], [1, 1, 1, 2, 2, 2,
747
+ 1, 1, 1, 2, 2, 2,
748
+ 3, 3, 3, 4, 4, 4,
749
+ 3, 3, 3, 4, 4, 4], dtype: dtype, stype: stype)
750
+ end
751
+ it "Compute the Kronecker product of two NMatrix" do
752
+ expect(@a.kron_prod(@b)).to eq(@c)
753
+ end
754
+ end
755
+ end
756
+ end
757
+
758
+ context "determinants" do
759
+ ALL_DTYPES.each do |dtype|
760
+ next if dtype == :object
761
+ context dtype do
762
+ before do
763
+ @a = NMatrix.new([2,2], [1,2,
764
+ 3,4], dtype: dtype)
765
+ @b = NMatrix.new([3,3], [1,2,3,
766
+ 5,0,1,
767
+ 4,1,3], dtype: dtype)
768
+ @c = NMatrix.new([4,4], [1, 0, 1, 1,
769
+ 1, 2, 3, 1,
770
+ 3, 3, 3, 1,
771
+ 1, 2, 3, 4], dtype: dtype)
772
+ @err = case dtype
773
+ when :float32, :complex64
774
+ 1e-6
775
+ when :float64, :complex128
776
+ 1e-14
777
+ else
778
+ 1e-64 # FIXME: should be 0, but be_within(0) does not work.
779
+ end
780
+ end
781
+ it "computes the determinant of 2x2 matrix" do
782
+ expect(@a.det).to be_within(@err).of(-2)
783
+ end
784
+ it "computes the determinant of 3x3 matrix" do
785
+ expect(@b.det).to be_within(@err).of(-8)
786
+ end
787
+ it "computes the determinant of 4x4 matrix" do
788
+ expect(@c.det).to be_within(@err).of(-18)
789
+ end
790
+ it "computes the exact determinant of 2x2 matrix" do
791
+ if dtype == :byte
792
+ expect{@a.det_exact}.to raise_error(DataTypeError)
793
+ else
794
+ expect(@a.det_exact).to be_within(@err).of(-2)
795
+ end
796
+ end
797
+ it "computes the exact determinant of 3x3 matrix" do
798
+ if dtype == :byte
799
+ expect{@a.det_exact}.to raise_error(DataTypeError)
800
+ else
801
+ expect(@b.det_exact).to be_within(@err).of(-8)
802
+ end
803
+ end
804
+ end
805
+ end
806
+ end
807
+ end