pnmatrix 1.2.4

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 (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