pnmatrix 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/ext/nmatrix/binary_format.txt +53 -0
  3. data/ext/nmatrix/data/complex.h +388 -0
  4. data/ext/nmatrix/data/data.cpp +274 -0
  5. data/ext/nmatrix/data/data.h +651 -0
  6. data/ext/nmatrix/data/meta.h +64 -0
  7. data/ext/nmatrix/data/ruby_object.h +386 -0
  8. data/ext/nmatrix/extconf.rb +70 -0
  9. data/ext/nmatrix/math/asum.h +99 -0
  10. data/ext/nmatrix/math/cblas_enums.h +36 -0
  11. data/ext/nmatrix/math/cblas_templates_core.h +507 -0
  12. data/ext/nmatrix/math/gemm.h +241 -0
  13. data/ext/nmatrix/math/gemv.h +178 -0
  14. data/ext/nmatrix/math/getrf.h +255 -0
  15. data/ext/nmatrix/math/getrs.h +121 -0
  16. data/ext/nmatrix/math/imax.h +82 -0
  17. data/ext/nmatrix/math/laswp.h +165 -0
  18. data/ext/nmatrix/math/long_dtype.h +62 -0
  19. data/ext/nmatrix/math/magnitude.h +54 -0
  20. data/ext/nmatrix/math/math.h +751 -0
  21. data/ext/nmatrix/math/nrm2.h +165 -0
  22. data/ext/nmatrix/math/rot.h +117 -0
  23. data/ext/nmatrix/math/rotg.h +106 -0
  24. data/ext/nmatrix/math/scal.h +71 -0
  25. data/ext/nmatrix/math/trsm.h +336 -0
  26. data/ext/nmatrix/math/util.h +162 -0
  27. data/ext/nmatrix/math.cpp +1368 -0
  28. data/ext/nmatrix/nm_memory.h +60 -0
  29. data/ext/nmatrix/nmatrix.cpp +285 -0
  30. data/ext/nmatrix/nmatrix.h +476 -0
  31. data/ext/nmatrix/ruby_constants.cpp +151 -0
  32. data/ext/nmatrix/ruby_constants.h +106 -0
  33. data/ext/nmatrix/ruby_nmatrix.c +3130 -0
  34. data/ext/nmatrix/storage/common.cpp +77 -0
  35. data/ext/nmatrix/storage/common.h +183 -0
  36. data/ext/nmatrix/storage/dense/dense.cpp +1096 -0
  37. data/ext/nmatrix/storage/dense/dense.h +129 -0
  38. data/ext/nmatrix/storage/list/list.cpp +1628 -0
  39. data/ext/nmatrix/storage/list/list.h +138 -0
  40. data/ext/nmatrix/storage/storage.cpp +730 -0
  41. data/ext/nmatrix/storage/storage.h +99 -0
  42. data/ext/nmatrix/storage/yale/class.h +1139 -0
  43. data/ext/nmatrix/storage/yale/iterators/base.h +143 -0
  44. data/ext/nmatrix/storage/yale/iterators/iterator.h +131 -0
  45. data/ext/nmatrix/storage/yale/iterators/row.h +450 -0
  46. data/ext/nmatrix/storage/yale/iterators/row_stored.h +140 -0
  47. data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +169 -0
  48. data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +124 -0
  49. data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
  50. data/ext/nmatrix/storage/yale/yale.cpp +2074 -0
  51. data/ext/nmatrix/storage/yale/yale.h +203 -0
  52. data/ext/nmatrix/types.h +55 -0
  53. data/ext/nmatrix/util/io.cpp +279 -0
  54. data/ext/nmatrix/util/io.h +115 -0
  55. data/ext/nmatrix/util/sl_list.cpp +627 -0
  56. data/ext/nmatrix/util/sl_list.h +144 -0
  57. data/ext/nmatrix/util/util.h +78 -0
  58. data/lib/nmatrix/blas.rb +378 -0
  59. data/lib/nmatrix/cruby/math.rb +744 -0
  60. data/lib/nmatrix/enumerate.rb +253 -0
  61. data/lib/nmatrix/homogeneous.rb +241 -0
  62. data/lib/nmatrix/io/fortran_format.rb +138 -0
  63. data/lib/nmatrix/io/harwell_boeing.rb +221 -0
  64. data/lib/nmatrix/io/market.rb +263 -0
  65. data/lib/nmatrix/io/point_cloud.rb +189 -0
  66. data/lib/nmatrix/jruby/decomposition.rb +24 -0
  67. data/lib/nmatrix/jruby/enumerable.rb +13 -0
  68. data/lib/nmatrix/jruby/error.rb +4 -0
  69. data/lib/nmatrix/jruby/math.rb +501 -0
  70. data/lib/nmatrix/jruby/nmatrix_java.rb +840 -0
  71. data/lib/nmatrix/jruby/operators.rb +283 -0
  72. data/lib/nmatrix/jruby/slice.rb +264 -0
  73. data/lib/nmatrix/lapack_core.rb +181 -0
  74. data/lib/nmatrix/lapack_plugin.rb +44 -0
  75. data/lib/nmatrix/math.rb +953 -0
  76. data/lib/nmatrix/mkmf.rb +100 -0
  77. data/lib/nmatrix/monkeys.rb +137 -0
  78. data/lib/nmatrix/nmatrix.rb +1172 -0
  79. data/lib/nmatrix/rspec.rb +75 -0
  80. data/lib/nmatrix/shortcuts.rb +1163 -0
  81. data/lib/nmatrix/version.rb +39 -0
  82. data/lib/nmatrix/yale_functions.rb +118 -0
  83. data/lib/nmatrix.rb +28 -0
  84. data/spec/00_nmatrix_spec.rb +892 -0
  85. data/spec/01_enum_spec.rb +196 -0
  86. data/spec/02_slice_spec.rb +407 -0
  87. data/spec/03_nmatrix_monkeys_spec.rb +80 -0
  88. data/spec/2x2_dense_double.mat +0 -0
  89. data/spec/4x4_sparse.mat +0 -0
  90. data/spec/4x5_dense.mat +0 -0
  91. data/spec/blas_spec.rb +215 -0
  92. data/spec/elementwise_spec.rb +311 -0
  93. data/spec/homogeneous_spec.rb +100 -0
  94. data/spec/io/fortran_format_spec.rb +88 -0
  95. data/spec/io/harwell_boeing_spec.rb +98 -0
  96. data/spec/io/test.rua +9 -0
  97. data/spec/io_spec.rb +159 -0
  98. data/spec/lapack_core_spec.rb +482 -0
  99. data/spec/leakcheck.rb +16 -0
  100. data/spec/math_spec.rb +1363 -0
  101. data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
  102. data/spec/nmatrix_yale_spec.rb +286 -0
  103. data/spec/rspec_monkeys.rb +56 -0
  104. data/spec/rspec_spec.rb +35 -0
  105. data/spec/shortcuts_spec.rb +474 -0
  106. data/spec/slice_set_spec.rb +162 -0
  107. data/spec/spec_helper.rb +172 -0
  108. data/spec/stat_spec.rb +214 -0
  109. data/spec/test.pcd +20 -0
  110. data/spec/utm5940.mtx +83844 -0
  111. metadata +295 -0
data/spec/math_spec.rb ADDED
@@ -0,0 +1,1363 @@
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 unless jruby? and dtype == :object
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
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
138
+
139
+ expect(@m.send(meth)).to eq N.new(@size, @a.map { |e| e.send(meth) }, dtype: dtype, stype: stype)
140
+
141
+ if dtype == :object
142
+ expect(@m.send(meth).dtype).to eq :object
143
+ else
144
+ expect(@m.send(meth).integer_dtype?).to eq true
145
+ end
146
+ end
147
+ elsif dtype.to_s.match(/float/)
148
+ it "should return dtype int64 for #{dtype}" do
149
+
150
+ expect(@m.send(meth)).to eq N.new(@size, @a.map { |e| e.send(meth) }, dtype: dtype, stype: stype)
151
+
152
+ expect(@m.send(meth).dtype).to eq :int64
153
+ end
154
+ elsif dtype.to_s.match(/complex/)
155
+ it "should properly calculate #{meth} for #{dtype}" do
156
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
157
+
158
+ 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)
159
+
160
+ expect(@m.send(meth).dtype).to eq :complex64 if dtype == :complex64
161
+ expect(@m.send(meth).dtype).to eq :complex128 if dtype == :complex128
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ context "#round for #{stype}" do
170
+ ALL_DTYPES.each do |dtype|
171
+ context dtype do
172
+ before :each do
173
+ @size = [2,2]
174
+ @mat = NMatrix.new @size, [1.33334, 0.9998, 1.9999, -8.9999],
175
+ dtype: dtype, stype: stype
176
+ @ans = @mat.to_a.flatten unless jruby? and dtype == :object
177
+ end
178
+
179
+ it "rounds" do
180
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
181
+ expect(@mat.round).to eq(N.new(@size, @ans.map { |a| a.round},
182
+ dtype: dtype, stype: stype))
183
+ end unless(/complex/ =~ dtype)
184
+
185
+ it "rounds with args" do
186
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
187
+ expect(@mat.round(2)).to eq(N.new(@size, @ans.map { |a| a.round(2)},
188
+ dtype: dtype, stype: stype))
189
+ end unless(/complex/ =~ dtype)
190
+
191
+ it "rounds complex with args" do
192
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
193
+ puts @mat.round(2)
194
+ expect(@mat.round(2)).to be_within(0.0001).of(N.new [2,2], @ans.map {|a|
195
+ Complex(a.real.round(2), a.imag.round(2))},dtype: dtype, stype: stype)
196
+ end if(/complex/ =~ dtype)
197
+
198
+ it "rounds complex" do
199
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
200
+ expect(@mat.round).to eq(N.new [2,2], @ans.map {|a|
201
+ Complex(a.real.round, a.imag.round)},dtype: dtype, stype: stype)
202
+ end if(/complex/ =~ dtype)
203
+ end
204
+ end
205
+ end
206
+
207
+ end
208
+ end
209
+ end
210
+
211
+ NON_INTEGER_DTYPES.each do |dtype|
212
+ context dtype do
213
+ before do
214
+ @m = NMatrix.new([3,4], GETRF_EXAMPLE_ARRAY, dtype: dtype)
215
+ @err = case dtype
216
+ when :float32, :complex64
217
+ 1e-6
218
+ when :float64, :complex128
219
+ 1e-14
220
+ end
221
+ end
222
+
223
+ #haven't check this spec yet. Also it doesn't check all the elements of the matrix.
224
+ it "should correctly factorize a matrix" do
225
+ pending("not yet implemented for :object dtype") if dtype == :object
226
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
227
+ a = @m.factorize_lu
228
+ expect(a).to be_within(@err).of(NMatrix.new([3,4], GETRF_SOLUTION_ARRAY, dtype: dtype))
229
+ end
230
+
231
+ it "also returns the permutation matrix" do
232
+ pending("not yet implemented for :object dtype") if dtype == :object
233
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
234
+
235
+ a, p = @m.factorize_lu perm_matrix: true
236
+
237
+ expect(a).to be_within(@err).of(NMatrix.new([3,4], GETRF_SOLUTION_ARRAY, dtype: dtype))
238
+
239
+ p_true = NMatrix.new([3,3], [0,0,1,1,0,0,0,1,0], dtype: dtype)
240
+ expect(p).to eq(p_true)
241
+ end
242
+ end
243
+ end
244
+
245
+ NON_INTEGER_DTYPES.each do |dtype|
246
+ context dtype do
247
+
248
+ it "calculates cholesky decomposition using potrf (lower)" do
249
+ #a = NMatrix.new([3,3],[1,1,1, 1,2,2, 1,2,6], dtype: dtype)
250
+ # We use the matrix
251
+ # 1 1 1
252
+ # 1 2 2
253
+ # 1 2 6
254
+ # which is symmetric and positive-definite as required, but
255
+ # we need only store the lower-half of the matrix.
256
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
257
+ pending("not yet implemented for :object dtype") if dtype == :object
258
+ a = NMatrix.new([3,3],[1,0,0, 1,2,0, 1,2,6], dtype: dtype)
259
+ begin
260
+ r = a.potrf!(:lower)
261
+
262
+ b = NMatrix.new([3,3],[1,0,0, 1,1,0, 1,1,2], dtype: dtype)
263
+ expect(a).to eq(b)
264
+ expect(r).to eq(b)
265
+ rescue NotImplementedError
266
+ pending "potrf! not implemented without plugins"
267
+ end
268
+ end
269
+
270
+ it "calculates cholesky decomposition using potrf (upper)" do
271
+ pending("not yet implemented for :object dtype") if dtype == :object
272
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
273
+
274
+ a = NMatrix.new([3,3],[1,1,1, 0,2,2, 0,0,6], dtype: dtype)
275
+ begin
276
+ r = a.potrf!(:upper)
277
+
278
+ b = NMatrix.new([3,3],[1,1,1, 0,1,1, 0,0,2], dtype: dtype)
279
+ expect(a).to eq(b)
280
+ expect(r).to eq(b)
281
+ rescue NotImplementedError
282
+ pending "potrf! not implemented without plugins"
283
+ end
284
+ end
285
+
286
+ it "calculates cholesky decomposition using #factorize_cholesky" do
287
+ pending("not yet implemented for :object dtype") if dtype == :object
288
+ a = NMatrix.new([3,3],[1,2,1, 2,13,5, 1,5,6], dtype: dtype)
289
+ begin
290
+ u,l = a.factorize_cholesky
291
+
292
+ l_true = NMatrix.new([3,3],[1,0,0, 2,3,0, 1,1,2], dtype: dtype)
293
+ u_true = l_true.transpose
294
+ expect(u).to eq(u_true)
295
+ expect(l).to eq(l_true)
296
+ rescue NotImplementedError
297
+ pending "potrf! not implemented without plugins"
298
+ end
299
+ end
300
+ end
301
+ end
302
+
303
+ NON_INTEGER_DTYPES.each do |dtype|
304
+ context dtype do
305
+
306
+ it "calculates QR decomposition using factorize_qr for a square matrix" do
307
+ pending("not yet implemented for :object dtype") if dtype == :object
308
+ a = NMatrix.new(3, [12.0, -51.0, 4.0,
309
+ 6.0, 167.0, -68.0,
310
+ -4.0, 24.0, -41.0] , dtype: dtype)
311
+
312
+ q_solution = NMatrix.new([3,3], Q_SOLUTION_ARRAY_2, dtype: dtype)
313
+
314
+ r_solution = NMatrix.new([3,3], [-14.0, -21.0, 14,
315
+ 0.0, -175, 70,
316
+ 0.0, 0.0, -35] , dtype: dtype)
317
+
318
+ err = case dtype
319
+ when :float32, :complex64
320
+ 1e-4
321
+ when :float64, :complex128
322
+ 1e-13
323
+ end
324
+
325
+ begin
326
+ q,r = a.factorize_qr
327
+
328
+ expect(q).to be_within(err).of(q_solution)
329
+ expect(r).to be_within(err).of(r_solution)
330
+
331
+ rescue NotImplementedError
332
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
333
+ end
334
+ end
335
+
336
+ it "calculates QR decomposition using factorize_qr for a tall and narrow rectangular matrix" do
337
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
338
+ pending("not yet implemented for :object dtype") if dtype == :object
339
+
340
+ a = NMatrix.new([4,2], [34.0, 21.0,
341
+ 23.0, 53.0,
342
+ 26.0, 346.0,
343
+ 23.0, 121.0] , dtype: dtype)
344
+
345
+ q_solution = NMatrix.new([4,4], Q_SOLUTION_ARRAY_1, dtype: dtype)
346
+
347
+ r_solution = NMatrix.new([4,2], [-53.75872022286244, -255.06559574252242,
348
+ 0.0, 269.34836526051555,
349
+ 0.0, 0.0,
350
+ 0.0, 0.0] , dtype: dtype)
351
+
352
+ err = case dtype
353
+ when :float32, :complex64
354
+ 1e-4
355
+ when :float64, :complex128
356
+ 1e-13
357
+ end
358
+
359
+ begin
360
+ q,r = a.factorize_qr
361
+
362
+ expect(q).to be_within(err).of(q_solution)
363
+ expect(r).to be_within(err).of(r_solution)
364
+
365
+ rescue NotImplementedError
366
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
367
+ end
368
+ end
369
+
370
+ it "calculates QR decomposition using factorize_qr for a short and wide rectangular matrix" do
371
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
372
+ pending("not yet implemented for :object dtype") if dtype == :object
373
+
374
+ a = NMatrix.new([3,4], [123,31,57,81,92,14,17,36,42,34,11,28], dtype: dtype)
375
+
376
+ q_solution = NMatrix.new([3,3], Q_SOLUTION_ARRAY_3, dtype: dtype)
377
+
378
+ r_solution = NMatrix.new([3,4], R_SOLUTION_ARRAY, dtype: dtype)
379
+
380
+ err = case dtype
381
+ when :float32, :complex64
382
+ 1e-4
383
+ when :float64, :complex128
384
+ 1e-13
385
+ end
386
+
387
+ begin
388
+ q,r = a.factorize_qr
389
+
390
+ expect(q).to be_within(err).of(q_solution)
391
+ expect(r).to be_within(err).of(r_solution)
392
+
393
+ rescue NotImplementedError
394
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
395
+ end
396
+ end
397
+
398
+ it "calculates QR decomposition such that A - QR ~ 0" do
399
+ pending("not yet implemented for :object dtype") if dtype == :object
400
+ a = NMatrix.new([3,3], [ 9.0, 0.0, 26.0,
401
+ 12.0, 0.0, -7.0,
402
+ 0.0, 4.0, 0.0] , dtype: dtype)
403
+
404
+ err = case dtype
405
+ when :float32, :complex64
406
+ 1e-4
407
+ when :float64, :complex128
408
+ 1e-13
409
+ end
410
+
411
+ begin
412
+ q,r = a.factorize_qr
413
+ a_expected = q.dot(r)
414
+
415
+ expect(a_expected).to be_within(err).of(a)
416
+
417
+ rescue NotImplementedError
418
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
419
+ end
420
+ end
421
+
422
+
423
+ it "calculates the orthogonal matrix Q in QR decomposition" do
424
+ pending("not yet implemented for :object dtype") if dtype == :object
425
+ a = N.new([2,2], [34.0, 21, 23, 53] , dtype: dtype)
426
+
427
+ err = case dtype
428
+ when :float32, :complex64
429
+ 1e-4
430
+ when :float64, :complex128
431
+ 1e-13
432
+ end
433
+
434
+ begin
435
+ q,r = a.factorize_qr
436
+
437
+ #Q is orthogonal if Q x Q.transpose = I
438
+ product = q.dot(q.transpose)
439
+
440
+ expect(product[0,0]).to be_within(err).of(1)
441
+ expect(product[1,0]).to be_within(err).of(0)
442
+ expect(product[0,1]).to be_within(err).of(0)
443
+ expect(product[1,1]).to be_within(err).of(1)
444
+
445
+ rescue NotImplementedError
446
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
447
+ end
448
+ end
449
+ end
450
+ end
451
+
452
+ ALL_DTYPES.each do |dtype|
453
+ next if dtype == :byte #doesn't work for unsigned types
454
+
455
+ context dtype do
456
+ err = case dtype
457
+ when :float32, :complex64
458
+ 1e-4
459
+ else #integer matrices will return :float64
460
+ 1e-13
461
+ end
462
+
463
+ it "should correctly invert a matrix in place (bang)" do
464
+ pending("not yet implemented for :object dtype") if dtype == :object
465
+ a = NMatrix.new(:dense, 5, [1, 8,-9, 7, 5,
466
+ 0, 1, 0, 4, 4,
467
+ 0, 0, 1, 2, 5,
468
+ 0, 0, 0, 1,-5,
469
+ 0, 0, 0, 0, 1 ], dtype)
470
+ b = NMatrix.new(:dense, 5, [1,-8, 9, 7, 17,
471
+ 0, 1, 0,-4,-24,
472
+ 0, 0, 1,-2,-15,
473
+ 0, 0, 0, 1, 5,
474
+ 0, 0, 0, 0, 1,], dtype)
475
+ if a.integer_dtype?
476
+ expect{a.invert!}.to raise_error(DataTypeError)
477
+ else
478
+ #should return inverse as well as modifying a
479
+ r = a.invert!
480
+ expect(a).to be_within(err).of(b)
481
+ expect(r).to be_within(err).of(b)
482
+ end
483
+ end
484
+
485
+
486
+ it "should correctly invert a dense matrix out-of-place" do
487
+ pending("not yet implemented for :object dtype") if dtype == :object
488
+ a = NMatrix.new(:dense, 3, [1,2,3,0,1,4,5,6,0], dtype)
489
+
490
+ if a.integer_dtype?
491
+ b = NMatrix.new(:dense, 3, [-24,18,5,20,-15,-4,-5,4,1], :float64)
492
+ else
493
+ b = NMatrix.new(:dense, 3, [-24,18,5,20,-15,-4,-5,4,1], dtype)
494
+ end
495
+
496
+ expect(a.invert).to be_within(err).of(b)
497
+ end
498
+
499
+ it "should correctly find exact inverse" do
500
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
501
+ a = NMatrix.new(:dense, 3, [1,2,3,0,1,4,5,6,0], dtype)
502
+ b = NMatrix.new(:dense, 3, [-24,18,5,20,-15,-4,-5,4,1], dtype)
503
+
504
+ expect(a.exact_inverse).to be_within(err).of(b)
505
+ end
506
+
507
+ it "should correctly find exact inverse" do
508
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
509
+ a = NMatrix.new(:dense, 2, [1,3,3,8], dtype)
510
+ b = NMatrix.new(:dense, 2, [-8,3,3,-1], dtype)
511
+
512
+ expect(a.exact_inverse).to be_within(err).of(b)
513
+ end
514
+ end
515
+ end
516
+
517
+ NON_INTEGER_DTYPES.each do |dtype|
518
+ context dtype do
519
+ err = Complex(1e-3, 1e-3)
520
+ it "should correctly invert a 2x2 matrix" do
521
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
522
+ pending("not yet implemented for :object dtype") if dtype == :object
523
+ if dtype == :complex64 || dtype == :complex128
524
+ a = NMatrix.new([2, 2], [Complex(16, 81), Complex(91, 51), \
525
+ Complex(13, 54), Complex(71, 24)], dtype: dtype)
526
+ b = NMatrix.identity(2, dtype: dtype)
527
+
528
+ begin
529
+ expect(a.dot(a.pinv)).to be_within(err).of(b)
530
+ rescue NotImplementedError
531
+ pending "Suppressing a NotImplementedError when the atlas plugin is not available"
532
+ end
533
+
534
+ else
535
+ a = NMatrix.new([2, 2], [141, 612, 9123, 654], dtype: dtype)
536
+ b = NMatrix.identity(2, dtype: dtype)
537
+
538
+ begin
539
+ expect(a.dot(a.pinv)).to be_within(err).of(b)
540
+ rescue NotImplementedError
541
+ pending "Suppressing a NotImplementedError when the atlas plugin is not available"
542
+ end
543
+ end
544
+ end
545
+
546
+ it "should verify a.dot(b.dot(a)) == a and b.dot(a.dot(b)) == b" do
547
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
548
+ pending("not yet implemented for :object dtype") if dtype == :object
549
+ if dtype == :complex64 || dtype == :complex128
550
+ a = NMatrix.new([3, 2], [Complex(94, 11), Complex(87, 51), Complex(82, 39), \
551
+ Complex(45, 16), Complex(25, 32), Complex(91, 43) ], dtype: dtype)
552
+
553
+ begin
554
+ b = a.pinv # pseudo inverse
555
+ expect(a.dot(b.dot(a))).to be_within(err).of(a)
556
+ expect(b.dot(a.dot(b))).to be_within(err).of(b)
557
+ rescue NotImplementedError
558
+ pending "Suppressing a NotImplementedError when the atlas plugin is not available"
559
+ end
560
+
561
+ else
562
+ a = NMatrix.new([3, 3], [9, 4, 52, 12, 52, 1, 3, 55, 6], dtype: dtype)
563
+
564
+ begin
565
+ b = a.pinv # pseudo inverse
566
+ expect(a.dot(b.dot(a))).to be_within(err).of(a)
567
+ expect(b.dot(a.dot(b))).to be_within(err).of(b)
568
+ rescue NotImplementedError
569
+ pending "Suppressing a NotImplementedError when the atlas plugin is not available"
570
+ end
571
+ end
572
+ end
573
+ end
574
+ end
575
+
576
+
577
+ ALL_DTYPES.each do |dtype|
578
+ next if dtype == :byte #doesn't work for unsigned types
579
+
580
+ context dtype do
581
+ err = case dtype
582
+ when :float32, :complex64
583
+ 1e-4
584
+ else #integer matrices will return :float64
585
+ 1e-13
586
+ end
587
+
588
+ it "should correctly find adjugate a matrix in place (bang)" do
589
+ pending("not yet implemented for :object dtype") if dtype == :object
590
+ a = NMatrix.new(:dense, 2, [2, 3, 3, 5], dtype)
591
+ b = NMatrix.new(:dense, 2, [5, -3, -3, 2], dtype)
592
+
593
+ if a.integer_dtype?
594
+ expect{a.adjugate!}.to raise_error(DataTypeError)
595
+ else
596
+ #should return adjugate as well as modifying a
597
+ r = a.adjugate!
598
+ expect(a).to be_within(err).of(b)
599
+ expect(r).to be_within(err).of(b)
600
+ end
601
+ end
602
+
603
+
604
+ it "should correctly find adjugate of a matrix out-of-place" do
605
+ pending("not yet implemented for :object dtype") if dtype == :object
606
+ a = NMatrix.new(:dense, 3, [-3, 2, -5, -1, 0, -2, 3, -4, 1], dtype)
607
+
608
+ if a.integer_dtype?
609
+ b = NMatrix.new(:dense, 3, [-8, 18, -4, -5, 12, -1, 4, -6, 2], :float64)
610
+ else
611
+ b = NMatrix.new(:dense, 3, [-8, 18, -4, -5, 12, -1, 4, -6, 2], dtype)
612
+ end
613
+
614
+ expect(a.adjoint).to be_within(err).of(b)
615
+ expect(a.adjugate).to be_within(err).of(b)
616
+ end
617
+
618
+ end
619
+ end
620
+
621
+
622
+ # TODO: Get it working with ROBJ too
623
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |left_dtype|
624
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |right_dtype|
625
+
626
+ # Won't work if they're both 1-byte, due to overflow.
627
+ next if [:byte,:int8].include?(left_dtype) && [:byte,:int8].include?(right_dtype)
628
+
629
+ # For now, don't bother testing int-int mult.
630
+ #next if [:int8,:int16,:int32,:int64].include?(left_dtype) && [:int8,:int16,:int32,:int64].include?(right_dtype)
631
+ it "dense handles #{left_dtype.to_s} dot #{right_dtype.to_s} matrix multiplication" do
632
+ #STDERR.puts "dtype=#{dtype.to_s}"
633
+ #STDERR.puts "2"
634
+
635
+ nary = if left_dtype.to_s =~ /complex/
636
+ COMPLEX_MATRIX43A_ARRAY
637
+ else
638
+ MATRIX43A_ARRAY
639
+ end
640
+
641
+ mary = if right_dtype.to_s =~ /complex/
642
+ COMPLEX_MATRIX32A_ARRAY
643
+ else
644
+ MATRIX32A_ARRAY
645
+ end
646
+
647
+ n = NMatrix.new([4,3], nary, dtype: left_dtype, stype: :dense)
648
+ m = NMatrix.new([3,2], mary, dtype: right_dtype, stype: :dense)
649
+
650
+ expect(m.shape[0]).to eq(3)
651
+ expect(m.shape[1]).to eq(2)
652
+ expect(m.dim).to eq(2)
653
+
654
+ expect(n.shape[0]).to eq(4)
655
+ expect(n.shape[1]).to eq(3)
656
+ expect(n.dim).to eq(2)
657
+
658
+ expect(n.shape[1]).to eq(m.shape[0])
659
+
660
+ r = n.dot m
661
+
662
+ expect(r[0,0]).to eq(273.0)
663
+ expect(r[0,1]).to eq(455.0)
664
+ expect(r[1,0]).to eq(243.0)
665
+ expect(r[1,1]).to eq(235.0)
666
+ expect(r[2,0]).to eq(244.0)
667
+ expect(r[2,1]).to eq(205.0)
668
+ expect(r[3,0]).to eq(102.0)
669
+ expect(r[3,1]).to eq(160.0)
670
+
671
+ #r.dtype.should == :float64 unless left_dtype == :float32 && right_dtype == :float32
672
+ end
673
+ end
674
+ end
675
+
676
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |left_dtype|
677
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |right_dtype|
678
+
679
+ # Won't work if they're both 1-byte, due to overflow.
680
+ next if [:byte,:int8].include?(left_dtype) && [:byte,:int8].include?(right_dtype)
681
+
682
+ it "dense handles #{left_dtype.to_s} dot #{right_dtype.to_s} vector multiplication" do
683
+ #STDERR.puts "dtype=#{dtype.to_s}"
684
+ #STDERR.puts "2"
685
+ 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)
686
+
687
+ m = NMatrix.new([3,1], [2.0, 1.0, 0.0], dtype: right_dtype)
688
+
689
+ expect(m.shape[0]).to eq(3)
690
+ expect(m.shape[1]).to eq(1)
691
+
692
+ expect(n.shape[0]).to eq(4)
693
+ expect(n.shape[1]).to eq(3)
694
+ expect(n.dim).to eq(2)
695
+
696
+ expect(n.shape[1]).to eq(m.shape[0])
697
+
698
+ r = n.dot m
699
+ # r.class.should == NVector
700
+
701
+ expect(r[0,0]).to eq(4)
702
+ expect(r[1,0]).to eq(13)
703
+ expect(r[2,0]).to eq(22)
704
+ expect(r[3,0]).to eq(31)
705
+
706
+ #r.dtype.should == :float64 unless left_dtype == :float32 && right_dtype == :float32
707
+ end
708
+ end
709
+ end
710
+
711
+ ALL_DTYPES.each do |dtype|
712
+ next if integer_dtype?(dtype)
713
+ context "#cov dtype #{dtype}" do
714
+ before do
715
+ @n = NMatrix.new( [5,3], [4.0,2.0,0.60,
716
+ 4.2,2.1,0.59,
717
+ 3.9,2.0,0.58,
718
+ 4.3,2.1,0.62,
719
+ 4.1,2.2,0.63], dtype: dtype)
720
+ end
721
+
722
+ it "calculates sample covariance matrix" do
723
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
724
+ expect(@n.cov).to be_within(0.0001).of(NMatrix.new([3,3],
725
+ [0.025 , 0.0075, 0.00175,
726
+ 0.0075, 0.007 , 0.00135,
727
+ 0.00175, 0.00135 , 0.00043 ], dtype: dtype)
728
+ )
729
+ end
730
+
731
+ it "calculates population covariance matrix" do
732
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
733
+ expect(@n.cov(for_sample_data: false)).to be_within(0.0001).of(NMatrix.new([3,3],
734
+ [2.0000e-02, 6.0000e-03, 1.4000e-03,
735
+ 6.0000e-03, 5.6000e-03, 1.0800e-03,
736
+ 1.4000e-03, 1.0800e-03, 3.4400e-04], dtype: dtype)
737
+ )
738
+ end
739
+ end
740
+
741
+ context "#corr #{dtype}" do
742
+ it "calculates the correlation matrix" do
743
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
744
+ n = NMatrix.new([5,3], [4.0,2.0,0.60,
745
+ 4.2,2.1,0.59,
746
+ 3.9,2.0,0.58,
747
+ 4.3,2.1,0.62,
748
+ 4.1,2.2,0.63], dtype: dtype)
749
+ expect(n.corr).to be_within(0.001).of(NMatrix.new([3,3],
750
+ [1.00000, 0.56695, 0.53374,
751
+ 0.56695, 1.00000, 0.77813,
752
+ 0.53374, 0.77813, 1.00000], dtype: dtype))
753
+ end unless dtype =~ /complex/
754
+ end
755
+
756
+ context "#symmetric? for #{dtype}" do
757
+ it "should return true for symmetric matrix" do
758
+ n = NMatrix.new([3,3], [1.00000, 0.56695, 0.53374,
759
+ 0.56695, 1.00000, 0.77813,
760
+ 0.53374, 0.77813, 1.00000], dtype: dtype)
761
+ expect(n.symmetric?).to be_truthy
762
+ end
763
+ end
764
+
765
+ context "#hermitian? for #{dtype}" do
766
+ it "should return true for complex hermitian or non-complex symmetric matrix" do
767
+ n = NMatrix.new([3,3], [1.00000, 0.56695, 0.53374,
768
+ 0.56695, 1.00000, 0.77813,
769
+ 0.53374, 0.77813, 1.00000], dtype: dtype) unless dtype =~ /complex/
770
+ n = NMatrix.new([3,3], [1.1, Complex(1.2,1.3), Complex(1.4,1.5),
771
+ Complex(1.2,-1.3), 1.9, Complex(1.8,1.7),
772
+ Complex(1.4,-1.5), Complex(1.8,-1.7), 1.3], dtype: dtype) if dtype =~ /complex/
773
+ expect(n.hermitian?).to be_truthy
774
+ end
775
+ end
776
+
777
+ context "#permute_columns for #{dtype}" do
778
+ it "check that #permute_columns works correctly by considering every premutation of a 3x3 matrix" do
779
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
780
+ n = NMatrix.new([3,3], [1,0,0,
781
+ 0,2,0,
782
+ 0,0,3], dtype: dtype)
783
+ expect(n.permute_columns([0,1,2], {convention: :intuitive})).to eq(NMatrix.new([3,3], [1,0,0,
784
+ 0,2,0,
785
+ 0,0,3], dtype: dtype))
786
+ expect(n.permute_columns([0,2,1], {convention: :intuitive})).to eq(NMatrix.new([3,3], [1,0,0,
787
+ 0,0,2,
788
+ 0,3,0], dtype: dtype))
789
+ expect(n.permute_columns([1,0,2], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,1,0,
790
+ 2,0,0,
791
+ 0,0,3], dtype: dtype))
792
+ expect(n.permute_columns([1,2,0], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,0,1,
793
+ 2,0,0,
794
+ 0,3,0], dtype: dtype))
795
+ expect(n.permute_columns([2,0,1], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,1,0,
796
+ 0,0,2,
797
+ 3,0,0], dtype: dtype))
798
+ expect(n.permute_columns([2,1,0], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,0,1,
799
+ 0,2,0,
800
+ 3,0,0], dtype: dtype))
801
+ expect(n.permute_columns([0,1,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [1,0,0,
802
+ 0,2,0,
803
+ 0,0,3], dtype: dtype))
804
+ expect(n.permute_columns([0,2,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [1,0,0,
805
+ 0,0,2,
806
+ 0,3,0], dtype: dtype))
807
+ expect(n.permute_columns([1,1,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,1,0,
808
+ 2,0,0,
809
+ 0,0,3], dtype: dtype))
810
+ expect(n.permute_columns([1,2,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,0,1,
811
+ 2,0,0,
812
+ 0,3,0], dtype: dtype))
813
+ expect(n.permute_columns([2,2,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,1,0,
814
+ 0,0,2,
815
+ 3,0,0], dtype: dtype))
816
+ expect(n.permute_columns([2,1,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,0,1,
817
+ 0,2,0,
818
+ 3,0,0], dtype: dtype))
819
+ end
820
+ it "additional tests for #permute_columns with convention :intuitive" do
821
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
822
+ m = NMatrix.new([1,4], [0,1,2,3], dtype: dtype)
823
+ perm = [1,0,3,2]
824
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,4], perm, dtype: dtype))
825
+
826
+ m = NMatrix.new([1,5], [0,1,2,3,4], dtype: dtype)
827
+ perm = [1,0,4,3,2]
828
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,5], perm, dtype: dtype))
829
+
830
+ m = NMatrix.new([1,6], [0,1,2,3,4,5], dtype: dtype)
831
+ perm = [2,4,1,0,5,3]
832
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,6], perm, dtype: dtype))
833
+
834
+ m = NMatrix.new([1,7], [0,1,2,3,4,5,6], dtype: dtype)
835
+ perm = [1,3,5,6,0,2,4]
836
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,7], perm, dtype: dtype))
837
+
838
+ m = NMatrix.new([1,8], [0,1,2,3,4,5,6,7], dtype: dtype)
839
+ perm = [6,7,5,4,1,3,0,2]
840
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,8], perm, dtype: dtype))
841
+ end
842
+ end
843
+ end
844
+
845
+ context "#solve" do
846
+ NON_INTEGER_DTYPES.each do |dtype|
847
+
848
+ it "solves linear equation for dtype #{dtype}" do
849
+ pending("not yet implemented for :object dtype") if dtype == :object
850
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
851
+ a = NMatrix.new [2,2], [3,1,1,2], dtype: dtype
852
+ b = NMatrix.new [2,1], [9,8], dtype: dtype
853
+
854
+ expect(a.solve(b)).to eq(NMatrix.new [2,1], [2,3], dtype: dtype)
855
+ end
856
+
857
+ it "solves linear equation for #{dtype} (non-symmetric matrix)" do
858
+ pending("not yet implemented for :object dtype") if dtype == :object
859
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
860
+
861
+ a = NMatrix.new [3,3], [1,1,1, -1,0,1, 3,4,6], dtype: dtype
862
+ b = NMatrix.new [3,1], [6,2,29], dtype: dtype
863
+
864
+ err = case dtype
865
+ when :float32, :complex64
866
+ 1e-5
867
+ else
868
+ 1e-14
869
+ end
870
+
871
+ expect(a.solve(b)).to be_within(err).of(NMatrix.new([3,1], [1,2,3], dtype: dtype))
872
+ end
873
+
874
+ it "solves linear equation for dtype #{dtype} (non-vector rhs)" do
875
+ pending("not yet implemented for :object dtype") if dtype == :object
876
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
877
+
878
+ a = NMatrix.new [3,3], [1,0,0, -1,0,1, 2,1,1], dtype: dtype
879
+ b = NMatrix.new [3,2], [1,0, 1,2, 4,2], dtype: dtype
880
+
881
+ expect(a.solve(b)).to eq(NMatrix.new [3,2], [1,0, 0,0, 2,2], dtype: dtype)
882
+ end
883
+ end
884
+
885
+ FLOAT_DTYPES.each do |dtype|
886
+ context "when form: :lower_tri" do
887
+ let(:a) { NMatrix.new([3,3], [1, 0, 0, 2, 0.5, 0, 3, 3, 9], dtype: dtype) }
888
+
889
+ it "solves a lower triangular linear system A * x = b with vector b" do
890
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
891
+ b = NMatrix.new([3,1], [1,2,3], dtype: dtype)
892
+ x = a.solve(b, form: :lower_tri)
893
+ r = a.dot(x) - b
894
+ expect(r.abs.max).to be_within(1e-6).of(0.0)
895
+ end
896
+
897
+ it "solves a lower triangular linear system A * X = B with narrow B" do
898
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
899
+ b = NMatrix.new([3,2], [1,2,3,4,5,6], dtype: dtype)
900
+ x = a.solve(b, form: :lower_tri)
901
+ r = (a.dot(x) - b).abs.to_flat_a
902
+ expect(r.max).to be_within(1e-6).of(0.0)
903
+ end
904
+
905
+ it "solves a lower triangular linear system A * X = B with wide B" do
906
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
907
+ b = NMatrix.new([3,5], (1..15).to_a, dtype: dtype)
908
+ x = a.solve(b, form: :lower_tri)
909
+ r = (a.dot(x) - b).abs.to_flat_a
910
+ expect(r.max).to be_within(1e-6).of(0.0)
911
+ end
912
+ end
913
+
914
+ context "when form: :upper_tri" do
915
+ let(:a) { NMatrix.new([3,3], [3, 2, 1, 0, 2, 0.5, 0, 0, 9], dtype: dtype) }
916
+
917
+ it "solves an upper triangular linear system A * x = b with vector b" do
918
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
919
+ b = NMatrix.new([3,1], [1,2,3], dtype: dtype)
920
+ x = a.solve(b, form: :upper_tri)
921
+ r = a.dot(x) - b
922
+ expect(r.abs.max).to be_within(1e-6).of(0.0)
923
+ end
924
+
925
+ it "solves an upper triangular linear system A * X = B with narrow B" do
926
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
927
+ b = NMatrix.new([3,2], [1,2,3,4,5,6], dtype: dtype)
928
+ x = a.solve(b, form: :upper_tri)
929
+ r = (a.dot(x) - b).abs.to_flat_a
930
+ expect(r.max).to be_within(1e-6).of(0.0)
931
+ end
932
+
933
+ it "solves an upper triangular linear system A * X = B with a wide B" do
934
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
935
+ b = NMatrix.new([3,5], (1..15).to_a, dtype: dtype)
936
+ x = a.solve(b, form: :upper_tri)
937
+ r = (a.dot(x) - b).abs.to_flat_a
938
+ expect(r.max).to be_within(1e-6).of(0.0)
939
+ end
940
+ end
941
+
942
+ context "when form: :pos_def" do
943
+ let(:a) { NMatrix.new([3,3], [4, 1, 2, 1, 5, 3, 2, 3, 6], dtype: dtype) }
944
+
945
+ it "solves a linear system A * X = b with positive definite A and vector b" do
946
+ b = NMatrix.new([3,1], [6,4,8], dtype: dtype)
947
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
948
+ begin
949
+ x = a.solve(b, form: :pos_def)
950
+ expect(x).to be_within(1e-6).of(NMatrix.new([3,1], [1,0,1], dtype: dtype))
951
+ rescue NotImplementedError
952
+ "Suppressing a NotImplementedError when the lapacke or atlas plugin is not available"
953
+ end
954
+ end
955
+
956
+ it "solves a linear system A * X = B with positive definite A and matrix B" do
957
+ b = NMatrix.new([3,2], [8,3,14,13,14,19], dtype: dtype)
958
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
959
+ begin
960
+ x = a.solve(b, form: :pos_def)
961
+ expect(x).to be_within(1e-6).of(NMatrix.new([3,2], [1,-1,2,1,1,3], dtype: dtype))
962
+ rescue NotImplementedError
963
+ "Suppressing a NotImplementedError when the lapacke or atlas plugin is not available"
964
+ end
965
+ end
966
+ end
967
+ end
968
+ end
969
+
970
+ context "#least_squares" do
971
+ it "finds the least squares approximation to the equation A * X = B" do
972
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
973
+ a = NMatrix.new([3,2], [2.0, 0, -1, 1, 0, 2])
974
+ b = NMatrix.new([3,1], [1.0, 0, -1])
975
+ solution = NMatrix.new([2,1], [1.0 / 3 , -1.0 / 3], dtype: :float64)
976
+
977
+ begin
978
+ least_squares = a.least_squares(b)
979
+ expect(least_squares).to be_within(0.0001).of solution
980
+ rescue NotImplementedError
981
+ "Suppressing a NotImplementedError when the lapacke or atlas plugin is not available"
982
+ end
983
+ end
984
+
985
+ it "finds the least squares approximation to the equation A * X = B with high tolerance" do
986
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
987
+ a = NMatrix.new([4,2], [1.0, 1, 1, 2, 1, 3,1,4])
988
+ b = NMatrix.new([4,1], [6.0, 5, 7, 10])
989
+ solution = NMatrix.new([2,1], [3.5 , 1.4], dtype: :float64)
990
+
991
+ begin
992
+ least_squares = a.least_squares(b, tolerance: 10e-5)
993
+ expect(least_squares).to be_within(0.0001).of solution
994
+ rescue NotImplementedError
995
+ "Suppressing a NotImplementedError when the lapacke or atlas plugin is not available"
996
+ end
997
+ end
998
+ end
999
+
1000
+ context "#hessenberg" do
1001
+ FLOAT_DTYPES.each do |dtype|
1002
+ context dtype do
1003
+ before do
1004
+ @n = NMatrix.new [5,5],
1005
+ [0, 2, 0, 1, 1,
1006
+ 2, 2, 3, 2, 2,
1007
+ 4,-3, 0, 1, 3,
1008
+ 6, 1,-6,-5, 4,
1009
+ 5, 6, 4, 1, 5], dtype: dtype
1010
+ end
1011
+
1012
+ it "transforms a matrix to Hessenberg form" do
1013
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1014
+ expect(@n.hessenberg).to be_within(0.0001).of(NMatrix.new([5,5],
1015
+ [0.00000,-1.66667, 0.79432,-0.45191,-1.54501,
1016
+ -9.00000, 2.95062,-6.89312, 3.22250,-0.19012,
1017
+ 0.00000,-8.21682,-0.57379, 5.26966,-1.69976,
1018
+ 0.00000, 0.00000,-3.74630,-0.80893, 3.99708,
1019
+ 0.00000, 0.00000, 0.00000, 0.04102, 0.43211], dtype: dtype))
1020
+ end
1021
+ end
1022
+ end
1023
+ end
1024
+
1025
+ ALL_DTYPES.each do |dtype|
1026
+ [:dense, :yale].each do |stype|
1027
+ answer_dtype = integer_dtype?(dtype) ? :int64 : dtype
1028
+ next if dtype == :byte
1029
+
1030
+ context "#pow #{dtype} #{stype}" do
1031
+ before do
1032
+ @n = NMatrix.new [4,4], [0, 2, 0, 1,
1033
+ 2, 2, 3, 2,
1034
+ 4,-3, 0, 1,
1035
+ 6, 1,-6,-5], dtype: dtype, stype: stype
1036
+ end
1037
+
1038
+ it "raises a square matrix to even power" do
1039
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
1040
+ expect(@n.pow(4)).to eq(NMatrix.new([4,4], [292, 28,-63, -42,
1041
+ 360, 96, 51, -14,
1042
+ 448,-231,-24,-87,
1043
+ -1168, 595,234, 523],
1044
+ dtype: answer_dtype,
1045
+ stype: stype))
1046
+ end
1047
+
1048
+ it "raises a square matrix to odd power" do
1049
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
1050
+ expect(@n.pow(9)).to eq(NMatrix.new([4,4],[-275128, 279917, 176127, 237451,
1051
+ -260104, 394759, 166893, 296081,
1052
+ -704824, 285700, 186411, 262002,
1053
+ 3209256,-1070870,-918741,-1318584],
1054
+ dtype: answer_dtype, stype: stype))
1055
+ end
1056
+
1057
+ it "raises a sqaure matrix to negative power" do
1058
+ expect(@n.pow(-3)).to be_within(0.00001).of (NMatrix.new([4,4],
1059
+ [1.0647e-02, 4.2239e-04,-6.2281e-05, 2.7680e-03,
1060
+ -1.6415e-02, 2.1296e-02, 1.0718e-02, 4.8589e-03,
1061
+ 8.6956e-03,-8.6569e-03, 2.8993e-02, 7.2015e-03,
1062
+ 5.0034e-02,-1.7500e-02,-3.6777e-02,-1.2128e-02], dtype: answer_dtype,
1063
+ stype: stype))
1064
+ end unless stype =~ /yale/ or dtype == :object or ALL_DTYPES.grep(/int/).include? dtype
1065
+
1066
+ it "raises a square matrix to zero" do
1067
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
1068
+ expect(@n.pow(0)).to eq(NMatrix.eye([4,4], dtype: answer_dtype,
1069
+ stype: stype))
1070
+ end
1071
+
1072
+ it "raises a square matrix to one" do
1073
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
1074
+ expect(@n.pow(1)).to eq(@n)
1075
+ end
1076
+ end
1077
+ end
1078
+ end
1079
+
1080
+ ALL_DTYPES.each do |dtype|
1081
+ [:dense, :yale].each do |stype|
1082
+ context "#kron_prod #{dtype} #{stype}" do
1083
+ before do
1084
+ @a = NMatrix.new([2,2], [1,2,
1085
+ 3,4], dtype: dtype, stype: stype)
1086
+ @b = NMatrix.new([2,3], [1,1,1,
1087
+ 1,1,1], dtype: dtype, stype: stype)
1088
+ @c = NMatrix.new([4,6], [1, 1, 1, 2, 2, 2,
1089
+ 1, 1, 1, 2, 2, 2,
1090
+ 3, 3, 3, 4, 4, 4,
1091
+ 3, 3, 3, 4, 4, 4], dtype: dtype, stype: stype)
1092
+ end
1093
+ it "computes the Kronecker product of two NMatrix objects" do
1094
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
1095
+ expect(@a.kron_prod(@b)).to eq(@c)
1096
+ end
1097
+ end
1098
+ end
1099
+ end
1100
+
1101
+ context "determinants" do
1102
+ ALL_DTYPES.each do |dtype|
1103
+ context dtype do
1104
+ pending("not yet implemented for :object dtype") if dtype == :object
1105
+ before do
1106
+ @a = NMatrix.new([2,2], [1,2,
1107
+ 3,4], dtype: dtype)
1108
+ @b = NMatrix.new([3,3], [1,2,3,
1109
+ 5,0,1,
1110
+ 4,1,3], dtype: dtype)
1111
+ @c = NMatrix.new([4,4], [1, 0, 1, 1,
1112
+ 1, 2, 3, 1,
1113
+ 3, 3, 3, 1,
1114
+ 1, 2, 3, 4], dtype: dtype)
1115
+ @err = case dtype
1116
+ when :float32, :complex64
1117
+ 1e-6
1118
+ when :float64, :complex128
1119
+ 1e-14
1120
+ else
1121
+ 1e-64 # FIXME: should be 0, but be_within(0) does not work.
1122
+ end
1123
+ end
1124
+ it "computes the determinant of 2x2 matrix" do
1125
+ pending("not yet implemented for :object dtype") if dtype == :object
1126
+ expect(@a.det).to be_within(@err).of(-2)
1127
+ end
1128
+ it "computes the determinant of 3x3 matrix" do
1129
+ pending("not yet implemented for :object dtype") if dtype == :object
1130
+ expect(@b.det).to be_within(@err).of(-8)
1131
+ end
1132
+ it "computes the determinant of 4x4 matrix" do
1133
+ pending("not yet implemented for :object dtype") if dtype == :object
1134
+ expect(@c.det).to be_within(@err).of(-18)
1135
+ end
1136
+ it "computes the exact determinant of 2x2 matrix" do
1137
+ pending("not yet implemented for :object dtype") if dtype == :object
1138
+ if dtype == :byte
1139
+ expect{@a.det_exact}.to raise_error(DataTypeError)
1140
+ else
1141
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
1142
+ expect(@a.det_exact).to be_within(@err).of(-2)
1143
+ end
1144
+ end
1145
+ it "computes the exact determinant of 3x3 matrix" do
1146
+ pending("not yet implemented for :object dtype") if dtype == :objectx
1147
+ if dtype == :byte
1148
+ expect{@a.det_exact}.to raise_error(DataTypeError)
1149
+ else
1150
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and dtype == :object
1151
+ expect(@b.det_exact).to be_within(@err).of(-8)
1152
+ end
1153
+ end
1154
+ end
1155
+ end
1156
+ end
1157
+
1158
+ context "#scale and #scale!" do
1159
+ [:dense,:list,:yale].each do |stype|
1160
+ ALL_DTYPES.each do |dtype|
1161
+ context "for #{dtype}" do
1162
+ before do
1163
+ @m = NMatrix.new([3, 3], [0, 1, 2,
1164
+ 3, 4, 5,
1165
+ 6, 7, 8], stype: stype, dtype: dtype)
1166
+ end
1167
+
1168
+ it "scales the matrix by a given factor and return the result" do
1169
+ pending("not yet implemented for :object dtype") if dtype == :object
1170
+ if integer_dtype? dtype
1171
+ expect{@m.scale 2.0}.to raise_error(DataTypeError)
1172
+ else
1173
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and (dtype == :complex64 || dtype == :complex128)
1174
+ expect(@m.scale 2.0).to eq(NMatrix.new([3, 3], [0, 2, 4,
1175
+ 6, 8, 10,
1176
+ 12, 14, 16], stype: stype, dtype: dtype))
1177
+ end
1178
+ end
1179
+
1180
+ it "scales the matrix in place by a given factor" do
1181
+ pending("not yet implemented for :object dtype") if dtype == :object
1182
+ if dtype == :int8
1183
+ expect{@m.scale! 2}.to raise_error(DataTypeError)
1184
+ else
1185
+ pending("not yet implemented for NMatrix-JRuby") if jruby? and (dtype == :complex64 || dtype == :complex128)
1186
+ @m.scale! 2
1187
+ expect(@m).to eq(NMatrix.new([3, 3], [0, 2, 4,
1188
+ 6, 8, 10,
1189
+ 12, 14, 16], stype: stype, dtype: dtype))
1190
+ end
1191
+ end
1192
+ end
1193
+ end
1194
+ end
1195
+ end
1196
+ context "matrix_norm" do
1197
+ ALL_DTYPES.each do |dtype|
1198
+ context dtype do
1199
+ pending("not yet implemented for :object dtype") if dtype == :object
1200
+ before do
1201
+ @n = NMatrix.new([3,3], [-4,-3,-2,
1202
+ -1, 0, 1,
1203
+ 2, 3, 4], dtype: dtype)
1204
+
1205
+ @matrix_norm_TOLERANCE = 1.0e-10
1206
+ end
1207
+
1208
+ it "should default to 2-matrix_norm" do
1209
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1210
+ if(dtype == :byte)
1211
+ expect{@n.matrix_norm}.to raise_error(ArgumentError)
1212
+ else
1213
+ begin
1214
+ expect(@n.matrix_norm).to be_within(@matrix_norm_TOLERANCE).of(7.348469228349535)
1215
+
1216
+ rescue NotImplementedError
1217
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1218
+ end
1219
+ end
1220
+ end
1221
+
1222
+ it "should reject invalid arguments" do
1223
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1224
+
1225
+ expect{@n.matrix_norm(0.5)}.to raise_error(ArgumentError)
1226
+ end
1227
+
1228
+ it "should calculate 1 and 2(minus) matrix_norms correctly" do
1229
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1230
+ if(dtype == :byte)
1231
+ expect{@n.matrix_norm(1)}.to raise_error(ArgumentError)
1232
+ expect{@n.matrix_norm(-2)}.to raise_error(ArgumentError)
1233
+ expect{@n.matrix_norm(-1)}.to raise_error(ArgumentError)
1234
+ else
1235
+ expect(@n.matrix_norm(1)).to eq(7)
1236
+ begin
1237
+
1238
+ #FIXME: change to the correct value when overflow issue is resolved
1239
+ #expect(@n.matrix_norm(-2)).to eq(1.8628605857884395e-07)
1240
+ expect(@n.matrix_norm(-2)).to be_within(@matrix_norm_TOLERANCE).of(0.0)
1241
+ rescue NotImplementedError
1242
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1243
+ end
1244
+ expect(@n.matrix_norm(-1)).to eq(6)
1245
+ end
1246
+ end
1247
+
1248
+ it "should calculate infinity matrix_norms correctly" do
1249
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1250
+ if(dtype == :byte)
1251
+ expect{@n.matrix_norm(:inf)}.to raise_error(ArgumentError)
1252
+ expect{@n.matrix_norm(:'-inf')}.to raise_error(ArgumentError)
1253
+ else
1254
+ expect(@n.matrix_norm(:inf)).to eq(9)
1255
+ expect(@n.matrix_norm(:'-inf')).to eq(2)
1256
+ end
1257
+ end
1258
+
1259
+ it "should calculate frobenius matrix_norms correctly" do
1260
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1261
+ if(dtype == :byte)
1262
+ expect{@n.matrix_norm(:fro)}.to raise_error(ArgumentError)
1263
+ else
1264
+ expect(@n.matrix_norm(:fro)).to be_within(@matrix_norm_TOLERANCE).of(7.745966692414834)
1265
+ end
1266
+ end
1267
+ end
1268
+ end
1269
+ end
1270
+
1271
+ context "#positive_definite?" do
1272
+ it "should return true for positive_definite? matrix" do
1273
+ n = NMatrix.new([3,3], [2, -1, -1,
1274
+ -1, 2, -1,
1275
+ -1, -1, 3])
1276
+ expect(n.positive_definite?).to be_truthy
1277
+ end
1278
+ end
1279
+
1280
+ context "#svd_rank" do
1281
+ FLOAT_DTYPES.each do |dtype|
1282
+ context dtype do
1283
+ #examples from https://www.cliffsnotes.com/study-guides/algebra/linear-algebra/real-euclidean-vector-spaces/the-rank-of-a-matrix
1284
+ it "calculates the rank of matrix using singular value decomposition with NMatrix on rectangular matrix without tolerence" do
1285
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1286
+ a = NMatrix.new([4,3],[2,-1,3, 1,0,1, 0,2,-1, 1,1,4], dtype: dtype)
1287
+
1288
+ begin
1289
+ rank = a.svd_rank()
1290
+
1291
+ rank_true = 3
1292
+ expect(rank).to eq (rank_true)
1293
+
1294
+ rescue NotImplementedError
1295
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1296
+ end
1297
+ end
1298
+
1299
+ it "calculates the rank of matrix using singular value decomposition with NMatrix on rectangular matrix with tolerence" do
1300
+
1301
+ a = NMatrix.new([4,3],[2,-1,3, 1,0,1, 0,2,-1, 1,1,4], dtype: dtype)
1302
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1303
+ begin
1304
+ rank = a.svd_rank(4)
1305
+
1306
+ rank_true = 1
1307
+ expect(rank).to eq (rank_true)
1308
+
1309
+ rescue NotImplementedError
1310
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1311
+ end
1312
+ end
1313
+
1314
+ it "calculates the rank of matrix using singular value decomposition with NMatrix on square matrix without tolerence" do
1315
+
1316
+ a = NMatrix.new([4,4],[1,-1,1,-1, -1,1,-1,1, 1,-1,1,-1, -1,1,-1,1], dtype: dtype)
1317
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1318
+ begin
1319
+ rank = a.svd_rank()
1320
+
1321
+ rank_true = 1
1322
+ expect(rank).to eq (rank_true)
1323
+
1324
+ rescue NotImplementedError
1325
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1326
+ end
1327
+ end
1328
+
1329
+ it "calculates the rank of matrix using singular value decomposition with NMatrix on square matrix with very small tolerence(for float32)" do
1330
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1331
+ a = NMatrix.new([4,4],[1,-1,1,-1, -1,1,-1,1, 1,-1,1,-1, -1,1,-1,1], dtype: :float32)
1332
+
1333
+ begin
1334
+ rank = a.svd_rank(1.7881389169360773e-08)
1335
+
1336
+ rank_true = 2
1337
+ expect(rank).to eq (rank_true)
1338
+
1339
+ rescue NotImplementedError
1340
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1341
+ end
1342
+ end
1343
+
1344
+ it "calculates the rank of matrix using singular value decomposition with NMatrix on square matrix with very small tolerence(for float64)" do
1345
+ pending("not yet implemented for NMatrix-JRuby") if jruby?
1346
+ a = NMatrix.new([4,4],[1,-1,1,-1, -1,1,-1,1, 1,-1,1,-1, -1,1,-1,1], dtype: :float64)
1347
+
1348
+ begin
1349
+ rank = a.svd_rank(1.7881389169360773e-08)
1350
+
1351
+ rank_true = 1
1352
+ expect(rank).to eq (rank_true)
1353
+
1354
+ rescue NotImplementedError
1355
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1356
+ end
1357
+ end
1358
+
1359
+ end
1360
+ end
1361
+ end
1362
+
1363
+ end