nmatrix 0.1.0 → 0.2.0

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/ext/nmatrix/data/complex.h +20 -55
  3. data/ext/nmatrix/data/data.cpp +11 -44
  4. data/ext/nmatrix/data/data.h +174 -311
  5. data/ext/nmatrix/data/meta.h +1 -7
  6. data/ext/nmatrix/data/ruby_object.h +3 -85
  7. data/ext/nmatrix/extconf.rb +2 -73
  8. data/ext/nmatrix/math.cpp +170 -813
  9. data/ext/nmatrix/math/asum.h +2 -25
  10. data/ext/nmatrix/math/{inc.h → cblas_enums.h} +11 -22
  11. data/ext/nmatrix/math/cblas_templates_core.h +507 -0
  12. data/ext/nmatrix/math/gemm.h +2 -32
  13. data/ext/nmatrix/math/gemv.h +1 -35
  14. data/ext/nmatrix/math/getrf.h +21 -6
  15. data/ext/nmatrix/math/getrs.h +0 -8
  16. data/ext/nmatrix/math/imax.h +0 -22
  17. data/ext/nmatrix/math/long_dtype.h +0 -3
  18. data/ext/nmatrix/math/math.h +11 -337
  19. data/ext/nmatrix/math/nrm2.h +2 -23
  20. data/ext/nmatrix/math/rot.h +1 -25
  21. data/ext/nmatrix/math/rotg.h +4 -13
  22. data/ext/nmatrix/math/scal.h +0 -22
  23. data/ext/nmatrix/math/trsm.h +0 -55
  24. data/ext/nmatrix/math/util.h +148 -0
  25. data/ext/nmatrix/nmatrix.cpp +0 -14
  26. data/ext/nmatrix/nmatrix.h +92 -84
  27. data/ext/nmatrix/ruby_constants.cpp +0 -2
  28. data/ext/nmatrix/ruby_constants.h +0 -2
  29. data/ext/nmatrix/ruby_nmatrix.c +86 -45
  30. data/ext/nmatrix/storage/dense/dense.cpp +1 -7
  31. data/ext/nmatrix/storage/storage.h +0 -1
  32. data/ext/nmatrix/ttable_helper.rb +0 -6
  33. data/ext/nmatrix/util/io.cpp +1 -1
  34. data/lib/nmatrix.rb +1 -19
  35. data/lib/nmatrix/blas.rb +33 -11
  36. data/lib/nmatrix/io/market.rb +3 -3
  37. data/lib/nmatrix/lapack_core.rb +181 -0
  38. data/lib/nmatrix/lapack_plugin.rb +44 -0
  39. data/lib/nmatrix/math.rb +382 -131
  40. data/lib/nmatrix/monkeys.rb +2 -3
  41. data/lib/nmatrix/nmatrix.rb +166 -13
  42. data/lib/nmatrix/shortcuts.rb +72 -7
  43. data/lib/nmatrix/version.rb +2 -2
  44. data/spec/00_nmatrix_spec.rb +154 -5
  45. data/spec/02_slice_spec.rb +2 -6
  46. data/spec/03_nmatrix_monkeys_spec.rb +7 -1
  47. data/spec/blas_spec.rb +60 -33
  48. data/spec/homogeneous_spec.rb +10 -10
  49. data/spec/lapack_core_spec.rb +482 -0
  50. data/spec/math_spec.rb +436 -52
  51. data/spec/shortcuts_spec.rb +28 -4
  52. data/spec/spec_helper.rb +14 -2
  53. data/spec/utm5940.mtx +83844 -0
  54. metadata +49 -76
  55. data/.gitignore +0 -27
  56. data/.rspec +0 -2
  57. data/.travis.yml +0 -15
  58. data/CONTRIBUTING.md +0 -82
  59. data/Gemfile +0 -2
  60. data/History.txt +0 -677
  61. data/LICENSE.txt +0 -23
  62. data/Manifest.txt +0 -92
  63. data/README.rdoc +0 -150
  64. data/Rakefile +0 -216
  65. data/ext/nmatrix/data/rational.h +0 -440
  66. data/ext/nmatrix/math/geev.h +0 -82
  67. data/ext/nmatrix/math/ger.h +0 -96
  68. data/ext/nmatrix/math/gesdd.h +0 -80
  69. data/ext/nmatrix/math/gesvd.h +0 -78
  70. data/ext/nmatrix/math/getf2.h +0 -86
  71. data/ext/nmatrix/math/getri.h +0 -108
  72. data/ext/nmatrix/math/potrs.h +0 -129
  73. data/ext/nmatrix/math/swap.h +0 -52
  74. data/lib/nmatrix/lapack.rb +0 -240
  75. data/nmatrix.gemspec +0 -55
  76. data/scripts/mac-brew-gcc.sh +0 -50
  77. data/scripts/mac-mavericks-brew-gcc.sh +0 -22
  78. data/spec/lapack_spec.rb +0 -459
@@ -28,16 +28,13 @@
28
28
 
29
29
  require 'spec_helper'
30
30
 
31
- ALL_DTYPES = [:byte,:int8,:int16,:int32,:int64, :float32,:float64, :object,
32
- :rational32,:rational64,:rational128, :complex64, :complex128]
33
-
34
31
  describe "math" do
35
32
  context "elementwise math functions" do
36
33
 
37
34
  [:dense,:list,:yale].each do |stype|
38
35
  context stype do
39
36
 
40
- [:int64,:float64,:rational128].each do |dtype|
37
+ [:int64,:float64].each do |dtype|
41
38
  context dtype do
42
39
  before :each do
43
40
  @size = [2,2]
@@ -146,7 +143,7 @@ describe "math" do
146
143
  expect(@m.send(meth).integer_dtype?).to eq true
147
144
  end
148
145
  end
149
- elsif dtype.to_s.match(/float/) or dtype.to_s.match(/rational/)
146
+ elsif dtype.to_s.match(/float/)
150
147
  it "should return dtype int64 for #{dtype}" do
151
148
 
152
149
  expect(@m.send(meth)).to eq N.new(@size, @a.map { |e| e.send(meth) }, dtype: dtype, stype: stype)
@@ -177,13 +174,23 @@ describe "math" do
177
174
  @ans = @mat.to_a.flatten
178
175
  end
179
176
 
180
- it "rounds #{dtype} for #{stype}" do
177
+ it "rounds" do
181
178
  expect(@mat.round).to eq(N.new(@size, @ans.map { |a| a.round},
182
179
  dtype: dtype, stype: stype))
183
180
  end unless(/complex/ =~ dtype)
184
181
 
185
- it "rounds complex dtype #{dtype} for #{stype}" do
186
-
182
+ it "rounds with args" do
183
+ expect(@mat.round(2)).to eq(N.new(@size, @ans.map { |a| a.round(2)},
184
+ dtype: dtype, stype: stype))
185
+ end unless(/complex/ =~ dtype)
186
+
187
+ it "rounds complex with args" do
188
+ puts @mat.round(2)
189
+ expect(@mat.round(2)).to be_within(0.0001).of(N.new [2,2], @ans.map {|a|
190
+ Complex(a.real.round(2), a.imag.round(2))},dtype: dtype, stype: stype)
191
+ end if(/complex/ =~ dtype)
192
+
193
+ it "rounds complex" do
187
194
  expect(@mat.round).to eq(N.new [2,2], @ans.map {|a|
188
195
  Complex(a.real.round, a.imag.round)},dtype: dtype, stype: stype)
189
196
  end if(/complex/ =~ dtype)
@@ -195,65 +202,139 @@ describe "math" do
195
202
  end
196
203
  end
197
204
 
198
- [:float32, :float64, :complex64, :complex128, :rational32, :rational64, :rational128].each do |dtype|
205
+ NON_INTEGER_DTYPES.each do |dtype|
206
+ next if dtype == :object
199
207
  context dtype do
208
+ before do
209
+ @m = NMatrix.new([3,4], GETRF_EXAMPLE_ARRAY, dtype: dtype)
210
+ @err = case dtype
211
+ when :float32, :complex64
212
+ 1e-6
213
+ when :float64, :complex128
214
+ 1e-14
215
+ end
216
+ end
217
+
218
+ #haven't check this spec yet. Also it doesn't check all the elements of the matrix.
200
219
  it "should correctly factorize a matrix" do
201
- m = NMatrix.new(:dense, 3, [4,9,2,3,5,7,8,1,6], dtype)
202
- a = m.factorize_lu
203
- expect(a[0,0]).to eq(8)
204
- expect(a[0,1]).to eq(1)
205
- expect(a[0,2]).to eq(6)
206
- expect(a[1,0]).to eq(0.5)
207
- expect(a[1,1]).to eq(8.5)
208
- expect(a[1,2]).to eq(-1)
209
- expect(a[2,0]).to eq(0.375)
220
+ a = @m.factorize_lu
221
+ expect(a).to be_within(@err).of(NMatrix.new([3,4], GETRF_SOLUTION_ARRAY, dtype: dtype))
222
+ end
223
+
224
+ it "also returns the permutation matrix" do
225
+ a, p = @m.factorize_lu perm_matrix: true
226
+
227
+ expect(a).to be_within(@err).of(NMatrix.new([3,4], GETRF_SOLUTION_ARRAY, dtype: dtype))
228
+
229
+ p_true = NMatrix.new([3,3], [0,0,1,1,0,0,0,1,0], dtype: dtype)
230
+ expect(p).to eq(p_true)
210
231
  end
211
232
  end
233
+ end
212
234
 
235
+ NON_INTEGER_DTYPES.each do |dtype|
236
+ next if dtype == :object
213
237
  context dtype do
214
- it "should correctly invert a matrix in place (bang)", :focus => true do
215
- a = NMatrix.new(:dense, 3, [1,2,3,0,1,4,5,6,0], dtype)
216
- b = NMatrix.new(:dense, 3, [-24,18,5,20,-15,-4,-5,4,1], dtype)
238
+
239
+ it "calculates cholesky decomposition using potrf (lower)" do
240
+ #a = NMatrix.new([3,3],[1,1,1, 1,2,2, 1,2,6], dtype: dtype)
241
+ # We use the matrix
242
+ # 1 1 1
243
+ # 1 2 2
244
+ # 1 2 6
245
+ # which is symmetric and positive-definite as required, but
246
+ # we need only store the lower-half of the matrix.
247
+ a = NMatrix.new([3,3],[1,0,0, 1,2,0, 1,2,6], dtype: dtype)
217
248
  begin
218
- a.invert!
219
- rescue NotImplementedError => e
220
- if dtype.to_s =~ /rational/
221
- pending "getri needs rational implementation"
222
- else
223
- pending e.to_s
224
- end
249
+ r = a.potrf!(:lower)
250
+
251
+ b = NMatrix.new([3,3],[1,0,0, 1,1,0, 1,1,2], dtype: dtype)
252
+ expect(a).to eq(b)
253
+ expect(r).to eq(b)
254
+ rescue NotImplementedError
255
+ pending "potrf! not implemented without plugins"
256
+ end
257
+ end
258
+
259
+ it "calculates cholesky decomposition using potrf (upper)" do
260
+ a = NMatrix.new([3,3],[1,1,1, 0,2,2, 0,0,6], dtype: dtype)
261
+ begin
262
+ r = a.potrf!(:upper)
263
+
264
+ b = NMatrix.new([3,3],[1,1,1, 0,1,1, 0,0,2], dtype: dtype)
265
+ expect(a).to eq(b)
266
+ expect(r).to eq(b)
267
+ rescue NotImplementedError
268
+ pending "potrf! not implemented without plugins"
225
269
  end
226
- expect(a.round).to eq(b)
227
270
  end
228
271
 
229
- unless NMatrix.has_clapack?
230
- it "should correctly invert a matrix in place" do
231
- a = NMatrix.new(:dense, 5, [1, 8,-9, 7, 5,
232
- 0, 1, 0, 4, 4,
233
- 0, 0, 1, 2, 5,
234
- 0, 0, 0, 1,-5,
235
- 0, 0, 0, 0, 1 ], dtype)
236
- b = NMatrix.new(:dense, 5, [1,-8, 9, 7, 17,
237
- 0, 1, 0,-4,-24,
238
- 0, 0, 1,-2,-15,
239
- 0, 0, 0, 1, 5,
240
- 0, 0, 0, 0, 1,], dtype)
241
- expect(a.invert).to eq(b)
272
+ it "calculates cholesky decomposition using #factorize_cholesky" do
273
+ a = NMatrix.new([3,3],[1,2,1, 2,13,5, 1,5,6], dtype: dtype)
274
+ begin
275
+ u,l = a.factorize_cholesky
276
+
277
+ l_true = NMatrix.new([3,3],[1,0,0, 2,3,0, 1,1,2], dtype: dtype)
278
+ u_true = l_true.transpose
279
+ expect(u).to eq(u_true)
280
+ expect(l).to eq(l_true)
281
+ rescue NotImplementedError
282
+ pending "potrf! not implemented without plugins"
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ ALL_DTYPES.each do |dtype|
289
+ next if dtype == :byte #doesn't work for unsigned types
290
+ next if dtype == :object
291
+
292
+ context dtype do
293
+ err = case dtype
294
+ when :float32, :complex64
295
+ 1e-4
296
+ else #integer matrices will return :float64
297
+ 1e-13
298
+ end
299
+
300
+ it "should correctly invert a matrix in place (bang)" do
301
+ a = NMatrix.new(:dense, 5, [1, 8,-9, 7, 5,
302
+ 0, 1, 0, 4, 4,
303
+ 0, 0, 1, 2, 5,
304
+ 0, 0, 0, 1,-5,
305
+ 0, 0, 0, 0, 1 ], dtype)
306
+ b = NMatrix.new(:dense, 5, [1,-8, 9, 7, 17,
307
+ 0, 1, 0,-4,-24,
308
+ 0, 0, 1,-2,-15,
309
+ 0, 0, 0, 1, 5,
310
+ 0, 0, 0, 0, 1,], dtype)
311
+ if a.integer_dtype?
312
+ expect{a.invert!}.to raise_error(DataTypeError)
313
+ else
314
+ #should return inverse as well as modifying a
315
+ r = a.invert!
316
+ expect(a).to be_within(err).of(b)
317
+ expect(r).to be_within(err).of(b)
242
318
  end
243
319
  end
244
320
 
245
321
  it "should correctly invert a matrix out-of-place" do
246
322
  a = NMatrix.new(:dense, 3, [1,2,3,0,1,4,5,6,0], dtype)
247
- b = NMatrix.new(:dense, 3, [-24,18,5,20,-15,-4,-5,4,1], dtype)
248
323
 
249
- expect(a.invert(3,3)).to eq(b)
324
+ if a.integer_dtype?
325
+ b = NMatrix.new(:dense, 3, [-24,18,5,20,-15,-4,-5,4,1], :float64)
326
+ else
327
+ b = NMatrix.new(:dense, 3, [-24,18,5,20,-15,-4,-5,4,1], dtype)
328
+ end
329
+
330
+ expect(a.invert).to be_within(err).of(b)
250
331
  end
251
332
  end
252
333
  end
253
334
 
254
335
  # TODO: Get it working with ROBJ too
255
- [:byte,:int8,:int16,:int32,:int64,:float32,:float64,:rational64,:rational128].each do |left_dtype|
256
- [:byte,:int8,:int16,:int32,:int64,:float32,:float64,:rational64,:rational128].each do |right_dtype|
336
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |left_dtype|
337
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |right_dtype|
257
338
 
258
339
  # Won't work if they're both 1-byte, due to overflow.
259
340
  next if [:byte,:int8].include?(left_dtype) && [:byte,:int8].include?(right_dtype)
@@ -266,16 +347,12 @@ describe "math" do
266
347
 
267
348
  nary = if left_dtype.to_s =~ /complex/
268
349
  COMPLEX_MATRIX43A_ARRAY
269
- elsif left_dtype.to_s =~ /rational/
270
- RATIONAL_MATRIX43A_ARRAY
271
350
  else
272
351
  MATRIX43A_ARRAY
273
352
  end
274
353
 
275
354
  mary = if right_dtype.to_s =~ /complex/
276
355
  COMPLEX_MATRIX32A_ARRAY
277
- elsif right_dtype.to_s =~ /rational/
278
- RATIONAL_MATRIX32A_ARRAY
279
356
  else
280
357
  MATRIX32A_ARRAY
281
358
  end
@@ -309,8 +386,8 @@ describe "math" do
309
386
  end
310
387
  end
311
388
 
312
- [:byte,:int8,:int16,:int32,:int64,:float32,:float64,:rational64,:rational128].each do |left_dtype|
313
- [:byte,:int8,:int16,:int32,:int64,:float32,:float64,:rational64,:rational128].each do |right_dtype|
389
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |left_dtype|
390
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |right_dtype|
314
391
 
315
392
  # Won't work if they're both 1-byte, due to overflow.
316
393
  next if [:byte,:int8].include?(left_dtype) && [:byte,:int8].include?(right_dtype)
@@ -343,4 +420,311 @@ describe "math" do
343
420
  end
344
421
  end
345
422
  end
423
+
424
+ ALL_DTYPES.each do |dtype|
425
+ next if integer_dtype?(dtype)
426
+ context "#cov dtype #{dtype}" do
427
+ before do
428
+ @n = NMatrix.new( [5,3], [4.0,2.0,0.60,
429
+ 4.2,2.1,0.59,
430
+ 3.9,2.0,0.58,
431
+ 4.3,2.1,0.62,
432
+ 4.1,2.2,0.63], dtype: dtype)
433
+ end
434
+
435
+ it "calculates variance co-variance matrix (sample)" do
436
+ expect(@n.cov).to be_within(0.0001).of(NMatrix.new([3,3],
437
+ [0.025 , 0.0075, 0.00175,
438
+ 0.0075, 0.007 , 0.00135,
439
+ 0.00175, 0.00135 , 0.00043 ], dtype: dtype)
440
+ )
441
+ end
442
+
443
+ it "calculates variance co-variance matrix (population)" do
444
+ expect(@n.cov(for_sample_data: false)).to be_within(0.0001).of(NMatrix.new([3,3],
445
+ [2.0000e-02, 6.0000e-03, 1.4000e-03,
446
+ 6.0000e-03, 5.6000e-03, 1.0800e-03,
447
+ 1.4000e-03, 1.0800e-03, 3.4400e-04], dtype: dtype)
448
+ )
449
+ end
450
+ end
451
+
452
+ context "#corr #{dtype}" do
453
+ it "calculates the correlation matrix" do
454
+ n = NMatrix.new([5,3], [4.0,2.0,0.60,
455
+ 4.2,2.1,0.59,
456
+ 3.9,2.0,0.58,
457
+ 4.3,2.1,0.62,
458
+ 4.1,2.2,0.63], dtype: dtype)
459
+ expect(n.corr).to be_within(0.001).of(NMatrix.new([3,3],
460
+ [1.00000, 0.56695, 0.53374,
461
+ 0.56695, 1.00000, 0.77813,
462
+ 0.53374, 0.77813, 1.00000], dtype: dtype))
463
+ end unless dtype =~ /complex/
464
+ end
465
+
466
+ context "#symmetric? for #{dtype}" do
467
+ it "should return true for symmetric matrix" do
468
+ n = NMatrix.new([3,3], [1.00000, 0.56695, 0.53374,
469
+ 0.56695, 1.00000, 0.77813,
470
+ 0.53374, 0.77813, 1.00000], dtype: dtype)
471
+ expect(n.symmetric?).to be_truthy
472
+ end
473
+ end
474
+
475
+ context "#hermitian? for #{dtype}" do
476
+ it "should return true for complex hermitian or non-complex symmetric matrix" do
477
+ n = NMatrix.new([3,3], [1.00000, 0.56695, 0.53374,
478
+ 0.56695, 1.00000, 0.77813,
479
+ 0.53374, 0.77813, 1.00000], dtype: dtype) unless dtype =~ /complex/
480
+ n = NMatrix.new([3,3], [1.1, Complex(1.2,1.3), Complex(1.4,1.5),
481
+ Complex(1.2,-1.3), 1.9, Complex(1.8,1.7),
482
+ Complex(1.4,-1.5), Complex(1.8,-1.7), 1.3], dtype: dtype) if dtype =~ /complex/
483
+ expect(n.hermitian?).to be_truthy
484
+ end
485
+ end
486
+
487
+ context "#permute_columns for #{dtype}" do
488
+ it "check that #permute_columns works correctly by considering every premutation of a 3x3 matrix" do
489
+ n = NMatrix.new([3,3], [1,0,0,
490
+ 0,2,0,
491
+ 0,0,3], dtype: dtype)
492
+ expect(n.permute_columns([0,1,2], {convention: :intuitive})).to eq(NMatrix.new([3,3], [1,0,0,
493
+ 0,2,0,
494
+ 0,0,3], dtype: dtype))
495
+ expect(n.permute_columns([0,2,1], {convention: :intuitive})).to eq(NMatrix.new([3,3], [1,0,0,
496
+ 0,0,2,
497
+ 0,3,0], dtype: dtype))
498
+ expect(n.permute_columns([1,0,2], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,1,0,
499
+ 2,0,0,
500
+ 0,0,3], dtype: dtype))
501
+ expect(n.permute_columns([1,2,0], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,0,1,
502
+ 2,0,0,
503
+ 0,3,0], dtype: dtype))
504
+ expect(n.permute_columns([2,0,1], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,1,0,
505
+ 0,0,2,
506
+ 3,0,0], dtype: dtype))
507
+ expect(n.permute_columns([2,1,0], {convention: :intuitive})).to eq(NMatrix.new([3,3], [0,0,1,
508
+ 0,2,0,
509
+ 3,0,0], dtype: dtype))
510
+ expect(n.permute_columns([0,1,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [1,0,0,
511
+ 0,2,0,
512
+ 0,0,3], dtype: dtype))
513
+ expect(n.permute_columns([0,2,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [1,0,0,
514
+ 0,0,2,
515
+ 0,3,0], dtype: dtype))
516
+ expect(n.permute_columns([1,1,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,1,0,
517
+ 2,0,0,
518
+ 0,0,3], dtype: dtype))
519
+ expect(n.permute_columns([1,2,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,0,1,
520
+ 2,0,0,
521
+ 0,3,0], dtype: dtype))
522
+ expect(n.permute_columns([2,2,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,1,0,
523
+ 0,0,2,
524
+ 3,0,0], dtype: dtype))
525
+ expect(n.permute_columns([2,1,2], {convention: :lapack})).to eq(NMatrix.new([3,3], [0,0,1,
526
+ 0,2,0,
527
+ 3,0,0], dtype: dtype))
528
+ end
529
+ it "additional tests for #permute_columns with convention :intuitive" do
530
+ m = NMatrix.new([1,4], [0,1,2,3], dtype: dtype)
531
+ perm = [1,0,3,2]
532
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,4], perm, dtype: dtype))
533
+
534
+ m = NMatrix.new([1,5], [0,1,2,3,4], dtype: dtype)
535
+ perm = [1,0,4,3,2]
536
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,5], perm, dtype: dtype))
537
+
538
+ m = NMatrix.new([1,6], [0,1,2,3,4,5], dtype: dtype)
539
+ perm = [2,4,1,0,5,3]
540
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,6], perm, dtype: dtype))
541
+
542
+ m = NMatrix.new([1,7], [0,1,2,3,4,5,6], dtype: dtype)
543
+ perm = [1,3,5,6,0,2,4]
544
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,7], perm, dtype: dtype))
545
+
546
+ m = NMatrix.new([1,8], [0,1,2,3,4,5,6,7], dtype: dtype)
547
+ perm = [6,7,5,4,1,3,0,2]
548
+ expect(m.permute_columns(perm, {convention: :intuitive})).to eq(NMatrix.new([1,8], perm, dtype: dtype))
549
+ end
550
+ end
551
+ end
552
+
553
+ context "#solve" do
554
+ NON_INTEGER_DTYPES.each do |dtype|
555
+ next if dtype == :object # LU factorization doesnt work for :object yet
556
+ it "solves linear equation for dtype #{dtype}" do
557
+ a = NMatrix.new [2,2], [3,1,1,2], dtype: dtype
558
+ b = NMatrix.new [2,1], [9,8], dtype: dtype
559
+
560
+ expect(a.solve(b)).to eq(NMatrix.new [2,1], [2,3], dtype: dtype)
561
+ end
562
+
563
+ it "solves linear equation for #{dtype} (non-symmetric matrix)" do
564
+ a = NMatrix.new [3,3], [1,1,1, -1,0,1, 3,4,6], dtype: dtype
565
+ b = NMatrix.new [3,1], [6,2,29], dtype: dtype
566
+
567
+ err = case dtype
568
+ when :float32, :complex64
569
+ 1e-5
570
+ else
571
+ 1e-14
572
+ end
573
+
574
+ expect(a.solve(b)).to be_within(err).of(NMatrix.new([3,1], [1,2,3], dtype: dtype))
575
+ end
576
+
577
+ it "solves linear equation for dtype #{dtype} (non-vector rhs)" do
578
+ a = NMatrix.new [3,3], [1,0,0, -1,0,1, 2,1,1], dtype: dtype
579
+ b = NMatrix.new [3,2], [1,0, 1,2, 4,2], dtype: dtype
580
+
581
+ expect(a.solve(b)).to eq(NMatrix.new [3,2], [1,0, 0,0, 2,2], dtype: dtype)
582
+ end
583
+ end
584
+ end
585
+
586
+ context "#hessenberg" do
587
+ FLOAT_DTYPES.each do |dtype|
588
+ context dtype do
589
+ before do
590
+ @n = NMatrix.new [5,5],
591
+ [0, 2, 0, 1, 1,
592
+ 2, 2, 3, 2, 2,
593
+ 4,-3, 0, 1, 3,
594
+ 6, 1,-6,-5, 4,
595
+ 5, 6, 4, 1, 5], dtype: dtype
596
+ end
597
+
598
+ it "transforms a matrix to Hessenberg form" do
599
+ expect(@n.hessenberg).to be_within(0.0001).of(NMatrix.new([5,5],
600
+ [0.00000,-1.66667, 0.79432,-0.45191,-1.54501,
601
+ -9.00000, 2.95062,-6.89312, 3.22250,-0.19012,
602
+ 0.00000,-8.21682,-0.57379, 5.26966,-1.69976,
603
+ 0.00000, 0.00000,-3.74630,-0.80893, 3.99708,
604
+ 0.00000, 0.00000, 0.00000, 0.04102, 0.43211], dtype: dtype))
605
+ end
606
+ end
607
+ end
608
+ end
609
+
610
+ ALL_DTYPES.each do |dtype|
611
+ [:dense, :yale].each do |stype|
612
+ answer_dtype = integer_dtype?(dtype) ? :int64 : dtype
613
+ next if dtype == :byte
614
+
615
+ context "#pow #{dtype} #{stype}" do
616
+ before do
617
+ @n = NMatrix.new [4,4], [0, 2, 0, 1,
618
+ 2, 2, 3, 2,
619
+ 4,-3, 0, 1,
620
+ 6, 1,-6,-5], dtype: dtype, stype: stype
621
+ end
622
+
623
+ it "raises a square matrix to even power" do
624
+ expect(@n.pow(4)).to eq(NMatrix.new([4,4], [292, 28,-63, -42,
625
+ 360, 96, 51, -14,
626
+ 448,-231,-24,-87,
627
+ -1168, 595,234, 523],
628
+ dtype: answer_dtype,
629
+ stype: stype))
630
+ end
631
+
632
+ it "raises a square matrix to odd power" do
633
+ expect(@n.pow(9)).to eq(NMatrix.new([4,4],[-275128, 279917, 176127, 237451,
634
+ -260104, 394759, 166893, 296081,
635
+ -704824, 285700, 186411, 262002,
636
+ 3209256,-1070870,-918741,-1318584],
637
+ dtype: answer_dtype, stype: stype))
638
+ end
639
+
640
+ it "raises a sqaure matrix to negative power" do
641
+ expect(@n.pow(-3)).to be_within(0.00001).of (NMatrix.new([4,4],
642
+ [1.0647e-02, 4.2239e-04,-6.2281e-05, 2.7680e-03,
643
+ -1.6415e-02, 2.1296e-02, 1.0718e-02, 4.8589e-03,
644
+ 8.6956e-03,-8.6569e-03, 2.8993e-02, 7.2015e-03,
645
+ 5.0034e-02,-1.7500e-02,-3.6777e-02,-1.2128e-02], dtype: answer_dtype,
646
+ stype: stype))
647
+ end unless stype =~ /yale/ or dtype == :object or ALL_DTYPES.grep(/int/).include? dtype
648
+
649
+ it "raises a square matrix to zero" do
650
+ expect(@n.pow(0)).to eq(NMatrix.eye([4,4], dtype: answer_dtype,
651
+ stype: stype))
652
+ end
653
+
654
+ it "raises a square matrix to one" do
655
+ expect(@n.pow(1)).to eq(@n)
656
+ end
657
+ end
658
+ end
659
+ end
660
+
661
+ ALL_DTYPES.each do |dtype|
662
+ [:dense, :yale].each do |stype|
663
+ context "#kron_prod #{dtype} #{stype}" do
664
+ before do
665
+ @a = NMatrix.new([2,2], [1,2,
666
+ 3,4], dtype: dtype, stype: stype)
667
+ @b = NMatrix.new([2,3], [1,1,1,
668
+ 1,1,1], dtype: dtype, stype: stype)
669
+ @c = NMatrix.new([4,6], [1, 1, 1, 2, 2, 2,
670
+ 1, 1, 1, 2, 2, 2,
671
+ 3, 3, 3, 4, 4, 4,
672
+ 3, 3, 3, 4, 4, 4], dtype: dtype, stype: stype)
673
+ end
674
+ it "Compute the Kronecker product of two NMatrix" do
675
+ expect(@a.kron_prod(@b)).to eq(@c)
676
+ end
677
+ end
678
+ end
679
+ end
680
+
681
+ context "determinants" do
682
+ ALL_DTYPES.each do |dtype|
683
+ next if dtype == :object
684
+ context dtype do
685
+ before do
686
+ @a = NMatrix.new([2,2], [1,2,
687
+ 3,4], dtype: dtype)
688
+ @b = NMatrix.new([3,3], [1,2,3,
689
+ 5,0,1,
690
+ 4,1,3], dtype: dtype)
691
+ @c = NMatrix.new([4,4], [1, 0, 1, 1,
692
+ 1, 2, 3, 1,
693
+ 3, 3, 3, 1,
694
+ 1, 2, 3, 4], dtype: dtype)
695
+ @err = case dtype
696
+ when :float32, :complex64
697
+ 1e-6
698
+ when :float64, :complex128
699
+ 1e-14
700
+ else
701
+ 1e-64 # FIXME: should be 0, but be_within(0) does not work.
702
+ end
703
+ end
704
+ it "computes the determinant of 2x2 matrix" do
705
+ expect(@a.det).to be_within(@err).of(-2)
706
+ end
707
+ it "computes the determinant of 3x3 matrix" do
708
+ expect(@b.det).to be_within(@err).of(-8)
709
+ end
710
+ it "computes the determinant of 4x4 matrix" do
711
+ expect(@c.det).to be_within(@err).of(-18)
712
+ end
713
+ it "computes the exact determinant of 2x2 matrix" do
714
+ if dtype == :byte
715
+ expect{@a.det_exact}.to raise_error(DataTypeError)
716
+ else
717
+ expect(@a.det_exact).to be_within(@err).of(-2)
718
+ end
719
+ end
720
+ it "computes the exact determinant of 3x3 matrix" do
721
+ if dtype == :byte
722
+ expect{@a.det_exact}.to raise_error(DataTypeError)
723
+ else
724
+ expect(@b.det_exact).to be_within(@err).of(-8)
725
+ end
726
+ end
727
+ end
728
+ end
729
+ end
346
730
  end