nmatrix-atlas 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/ext/nmatrix/data/complex.h +364 -0
  3. data/ext/nmatrix/data/data.h +638 -0
  4. data/ext/nmatrix/data/meta.h +64 -0
  5. data/ext/nmatrix/data/ruby_object.h +389 -0
  6. data/ext/nmatrix/math/asum.h +120 -0
  7. data/ext/nmatrix/math/cblas_enums.h +36 -0
  8. data/ext/nmatrix/math/cblas_templates_core.h +507 -0
  9. data/ext/nmatrix/math/gemm.h +241 -0
  10. data/ext/nmatrix/math/gemv.h +178 -0
  11. data/ext/nmatrix/math/getrf.h +255 -0
  12. data/ext/nmatrix/math/getrs.h +121 -0
  13. data/ext/nmatrix/math/imax.h +79 -0
  14. data/ext/nmatrix/math/laswp.h +165 -0
  15. data/ext/nmatrix/math/long_dtype.h +49 -0
  16. data/ext/nmatrix/math/math.h +744 -0
  17. data/ext/nmatrix/math/nrm2.h +160 -0
  18. data/ext/nmatrix/math/rot.h +117 -0
  19. data/ext/nmatrix/math/rotg.h +106 -0
  20. data/ext/nmatrix/math/scal.h +71 -0
  21. data/ext/nmatrix/math/trsm.h +332 -0
  22. data/ext/nmatrix/math/util.h +148 -0
  23. data/ext/nmatrix/nm_memory.h +60 -0
  24. data/ext/nmatrix/nmatrix.h +408 -0
  25. data/ext/nmatrix/ruby_constants.h +106 -0
  26. data/ext/nmatrix/storage/common.h +176 -0
  27. data/ext/nmatrix/storage/dense/dense.h +128 -0
  28. data/ext/nmatrix/storage/list/list.h +137 -0
  29. data/ext/nmatrix/storage/storage.h +98 -0
  30. data/ext/nmatrix/storage/yale/class.h +1139 -0
  31. data/ext/nmatrix/storage/yale/iterators/base.h +142 -0
  32. data/ext/nmatrix/storage/yale/iterators/iterator.h +130 -0
  33. data/ext/nmatrix/storage/yale/iterators/row.h +449 -0
  34. data/ext/nmatrix/storage/yale/iterators/row_stored.h +139 -0
  35. data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +168 -0
  36. data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +123 -0
  37. data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
  38. data/ext/nmatrix/storage/yale/yale.h +202 -0
  39. data/ext/nmatrix/types.h +54 -0
  40. data/ext/nmatrix/util/io.h +115 -0
  41. data/ext/nmatrix/util/sl_list.h +143 -0
  42. data/ext/nmatrix/util/util.h +78 -0
  43. data/ext/nmatrix_atlas/extconf.rb +250 -0
  44. data/ext/nmatrix_atlas/math_atlas.cpp +1206 -0
  45. data/ext/nmatrix_atlas/math_atlas/cblas_templates_atlas.h +72 -0
  46. data/ext/nmatrix_atlas/math_atlas/clapack_templates.h +332 -0
  47. data/ext/nmatrix_atlas/math_atlas/geev.h +82 -0
  48. data/ext/nmatrix_atlas/math_atlas/gesdd.h +83 -0
  49. data/ext/nmatrix_atlas/math_atlas/gesvd.h +81 -0
  50. data/ext/nmatrix_atlas/math_atlas/inc.h +47 -0
  51. data/ext/nmatrix_atlas/nmatrix_atlas.cpp +44 -0
  52. data/lib/nmatrix/atlas.rb +213 -0
  53. data/lib/nmatrix/lapack_ext_common.rb +69 -0
  54. data/spec/00_nmatrix_spec.rb +730 -0
  55. data/spec/01_enum_spec.rb +190 -0
  56. data/spec/02_slice_spec.rb +389 -0
  57. data/spec/03_nmatrix_monkeys_spec.rb +78 -0
  58. data/spec/2x2_dense_double.mat +0 -0
  59. data/spec/4x4_sparse.mat +0 -0
  60. data/spec/4x5_dense.mat +0 -0
  61. data/spec/blas_spec.rb +193 -0
  62. data/spec/elementwise_spec.rb +303 -0
  63. data/spec/homogeneous_spec.rb +99 -0
  64. data/spec/io/fortran_format_spec.rb +88 -0
  65. data/spec/io/harwell_boeing_spec.rb +98 -0
  66. data/spec/io/test.rua +9 -0
  67. data/spec/io_spec.rb +149 -0
  68. data/spec/lapack_core_spec.rb +482 -0
  69. data/spec/leakcheck.rb +16 -0
  70. data/spec/math_spec.rb +730 -0
  71. data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
  72. data/spec/nmatrix_yale_spec.rb +286 -0
  73. data/spec/plugins/atlas/atlas_spec.rb +242 -0
  74. data/spec/rspec_monkeys.rb +56 -0
  75. data/spec/rspec_spec.rb +34 -0
  76. data/spec/shortcuts_spec.rb +310 -0
  77. data/spec/slice_set_spec.rb +157 -0
  78. data/spec/spec_helper.rb +140 -0
  79. data/spec/stat_spec.rb +203 -0
  80. data/spec/test.pcd +20 -0
  81. data/spec/utm5940.mtx +83844 -0
  82. metadata +159 -0
@@ -0,0 +1,99 @@
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
+ # == homogeneous_spec.rb
24
+ #
25
+ # Specs for the homogeneous transformation matrix methods.
26
+ #
27
+
28
+ require 'spec_helper'
29
+ require "./lib/nmatrix/homogeneous.rb"
30
+
31
+ require 'pry'
32
+
33
+ describe 'NMatrix' do
34
+ context ".x_rotation" do
35
+ it "should generate a matrix representing a rotation about the x axis" do
36
+ x = NMatrix.x_rotation(Math::PI/6)
37
+ expect(x).to be_within(1e-8).of(NMatrix.new([4,4], [1.0, 0.0, 0.0, 0.0,
38
+ 0.0, Math.cos(Math::PI/6), -0.5, 0.0,
39
+ 0.0, 0.5, Math.cos(Math::PI/6), 0.0,
40
+ 0.0, 0.0, 0.0, 1.0] ))
41
+ end
42
+ end
43
+
44
+
45
+ context ".y_rotation" do
46
+ it "should generate a matrix representing a rotation about the y axis" do
47
+ y = NMatrix.y_rotation(Math::PI/6)
48
+ expect(y).to be_within(1e-8).of(NMatrix.new([4,4], [Math.cos(Math::PI/6), 0.0, 0.5, 0.0,
49
+ 0.0, 1.0, 0.0, 0.0,
50
+ -0.5, 0.0, Math.cos(Math::PI/6), 0.0,
51
+ 0.0, 0.0, 0.0, 1.0] ))
52
+ end
53
+ end
54
+
55
+ context ".z_rotation" do
56
+ it "should generate a matrix representing a rotation about the z axis" do
57
+ z = NMatrix.z_rotation(Math::PI/6)
58
+ expect(z).to be_within(1e-8).of(NMatrix.new([4,4], [Math.cos(Math::PI/6), -0.5, 0.0, 0.0,
59
+ 0.5, Math.cos(Math::PI/6), 0.0, 0.0,
60
+ 0.0, 0.0, 1.0, 0.0,
61
+ 0.0, 0.0, 0.0, 1.0] ))
62
+ end
63
+ end
64
+
65
+ context ".translation" do
66
+ it "should generate a translation matrix from an Array" do
67
+ t = NMatrix.translation([4,5,6])
68
+ expect(t).to be_within(1e-8).of(NMatrix.new([4,4], [1, 0, 0, 4,
69
+ 0, 1, 0, 5,
70
+ 0, 0, 1, 6,
71
+ 0, 0, 0, 1] ))
72
+ end
73
+
74
+ it "should generate a translation matrix from x, y, and z values" do
75
+ t = NMatrix.translation(4,5,6)
76
+ expect(t).to be_within(1e-8).of(NMatrix.new([4,4], [1, 0, 0, 4,
77
+ 0, 1, 0, 5,
78
+ 0, 0, 1, 6,
79
+ 0, 0, 0, 1] ))
80
+ end
81
+
82
+ it "should generate a translation matrix from an NMatrix with correctly inferred dtype" do
83
+ t = NMatrix.translation(NMatrix.new([3,1], [4,5,6], dtype: :float64) )
84
+ expect(t).to be_within(1e-8).of(NMatrix.new([4,4], [1, 0, 0, 4,
85
+ 0, 1, 0, 5,
86
+ 0, 0, 1, 6,
87
+ 0, 0, 0, 1] ))
88
+ expect(t.dtype).to be(:float64)
89
+ end
90
+ end
91
+
92
+ context "#quaternion" do
93
+ it "should generate a singularity-free quaternion" do
94
+ transform = NMatrix.new([4,4], [-0.9995825,-0.02527934,-0.0139845,50.61761,-0.02732551,0.9844284,0.1736463,-22.95566,0.009376526,0.1739562,-0.9847089,7.1521,0,0,0,1])
95
+ q = transform.quaternion
96
+ expect(Math.sqrt(q[0]**2 + q[1]**2 + q[2]**2 + q[3]**2)).to be_within(1e-6).of(1.0)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,88 @@
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
+ # == fortran_format_spec.rb
24
+ #
25
+ # Basic tests for NMatrix::IO::FortranFormat.
26
+ #
27
+
28
+ require './lib/nmatrix'
29
+
30
+ describe NMatrix::IO::FortranFormat do
31
+ it "parses integer FORTRAN formats" do
32
+ int_fmt = NMatrix::IO::FortranFormat::Reader.new('(16I5)').parse
33
+
34
+ expect(int_fmt[:format_code]).to eq "INT_ID"
35
+ expect(int_fmt[:repeat]) .to eq 16
36
+ expect(int_fmt[:field_width]).to eq 5
37
+
38
+ int_fmt = NMatrix::IO::FortranFormat::Reader.new('(I4)').parse
39
+
40
+ expect(int_fmt[:format_code]).to eq "INT_ID"
41
+ expect(int_fmt[:field_width]).to eq 4
42
+ end
43
+
44
+ it "parses floating point FORTRAN formats" do
45
+ fp_fmt = NMatrix::IO::FortranFormat::Reader.new('(10F7.1)').parse
46
+
47
+ expect(fp_fmt[:format_code]) .to eq "FP_ID"
48
+ expect(fp_fmt[:repeat]) .to eq 10
49
+ expect(fp_fmt[:field_width]) .to eq 7
50
+ expect(fp_fmt[:post_decimal_width]).to eq 1
51
+
52
+ fp_fmt = NMatrix::IO::FortranFormat::Reader.new('(F4.2)').parse
53
+
54
+ expect(fp_fmt[:format_code]) .to eq "FP_ID"
55
+ expect(fp_fmt[:field_width]) .to eq 4
56
+ expect(fp_fmt[:post_decimal_width]).to eq 2
57
+ end
58
+
59
+ it "parses exponential FORTRAN formats" do
60
+ exp_fmt = NMatrix::IO::FortranFormat::Reader.new('(2E8.3E3)').parse
61
+
62
+ expect(exp_fmt[:format_code]) .to eq "EXP_ID"
63
+ expect(exp_fmt[:repeat]) .to eq 2
64
+ expect(exp_fmt[:field_width]) .to eq 8
65
+ expect(exp_fmt[:post_decimal_width]).to eq 3
66
+ expect(exp_fmt[:exponent_width]) .to eq 3
67
+
68
+ exp_fmt = NMatrix::IO::FortranFormat::Reader.new('(3E3.6)').parse
69
+
70
+ expect(exp_fmt[:format_code]) .to eq "EXP_ID"
71
+ expect(exp_fmt[:repeat]) .to eq 3
72
+ expect(exp_fmt[:field_width]) .to eq 3
73
+ expect(exp_fmt[:post_decimal_width]).to eq 6
74
+
75
+ exp_fmt = NMatrix::IO::FortranFormat::Reader.new('(E4.5)').parse
76
+ expect(exp_fmt[:format_code]) .to eq "EXP_ID"
77
+ expect(exp_fmt[:field_width]) .to eq 4
78
+ expect(exp_fmt[:post_decimal_width]).to eq 5
79
+ end
80
+
81
+ ['I3', '(F4)', '(E3.', '(E4.E5)'].each do |bad_format|
82
+ it "doesn't let bad input through : #{bad_format}" do
83
+ expect {
84
+ NMatrix::IO::FortranFormat::Reader.new(bad_format).parse
85
+ }.to raise_error(IOError)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,98 @@
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
+ # == io_spec.rb
24
+ #
25
+ # Basic tests for NMatrix::IO::HarwelBoeing.
26
+
27
+ # TODO : After the fortran format thing is done
28
+ require 'spec_helper'
29
+ require "./lib/nmatrix"
30
+
31
+ describe NMatrix::IO::HarwellBoeing do
32
+ def check_file_header header
33
+ expect(header[:title]) .to eq("Title")
34
+ expect(header[:key]) .to eq("Key")
35
+
36
+ expect(header[:totcrd]) .to eq(5)
37
+ expect(header[:ptrcrd]) .to eq(1)
38
+ expect(header[:indcrd]) .to eq(1)
39
+ expect(header[:valcrd]) .to eq(3)
40
+ expect(header[:rhscrd]) .to eq(0)
41
+
42
+ expect(header[:mxtype]) .to eq('RUA')
43
+ expect(header[:nrow]) .to eq(5)
44
+ expect(header[:ncol]) .to eq(5)
45
+ expect(header[:nnzero]) .to eq(13)
46
+ expect(header[:neltvl]) .to eq(0)
47
+
48
+ expect(header[:ptrfmt]) .to eq({
49
+ format_code: "INT_ID",
50
+ repeat: 6,
51
+ field_width: 3
52
+ })
53
+ expect(header[:indfmt]) .to eq({
54
+ format_code: "INT_ID",
55
+ repeat: 13,
56
+ field_width: 3
57
+ })
58
+ expect(header[:valfmt]) .to eq({
59
+ format_code: "EXP_ID",
60
+ repeat: 5,
61
+ field_width: 15,
62
+ post_decimal_width: 8
63
+ })
64
+ expect(header[:rhsfmt]) .to eq({
65
+ format_code: "EXP_ID",
66
+ repeat: 5,
67
+ field_width: 15,
68
+ post_decimal_width: 8
69
+ })
70
+ end
71
+
72
+ it "loads a Harwell Boeing file values and header (currently real only)" do
73
+ n, h = NMatrix::IO::HarwellBoeing.load("spec/io/test.rua")
74
+
75
+ expect(n.is_a? NMatrix).to eq(true)
76
+ expect(n.cols) .to eq(5)
77
+ expect(n.rows) .to eq(5)
78
+
79
+ expect(n[0,0]) .to eq(11)
80
+ expect(n[4,4]) .to eq(55)
81
+
82
+ expect(h.is_a? Hash).to eq(true)
83
+ check_file_header(h)
84
+ end
85
+
86
+ it "loads only the header of the file when specified" do
87
+ h = NMatrix::IO::HarwellBoeing.load("spec/io/test.rua", header: true)
88
+
89
+ expect(h.is_a? Hash).to eq(true)
90
+ check_file_header(h)
91
+ end
92
+
93
+ it "raises error for wrong Harwell Boeing file name" do
94
+ expect{
95
+ NMatrix::IO::HarwellBoeing.load("spec/io/wrong.afx")
96
+ }.to raise_error(IOError)
97
+ end
98
+ end
@@ -0,0 +1,9 @@
1
+ Title Key
2
+ 5 1 1 3 0
3
+ RUA 5 5 13 0
4
+ (6I3) (13I3) (5E15.8) (5E15.8)
5
+ 1 4 7 8 11 14
6
+ 1 3 5 2 3 5 3 1 3 4 3 4 5
7
+ 11.0 31.0 51.0 22.0 32.0
8
+ 52.0 33.0 14.0 34.0 44.0
9
+ 35.0 45.0 55.0
@@ -0,0 +1,149 @@
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
+ # == io_spec.rb
24
+ #
25
+ # Basic tests for NMatrix::IO.
26
+ #
27
+ require "tmpdir" # Used to avoid cluttering the repository.
28
+ require 'spec_helper'
29
+ require "./lib/nmatrix"
30
+
31
+ describe NMatrix::IO do
32
+ let(:tmp_dir) { Dir.mktmpdir }
33
+ let(:test_out) { File.join(tmp_dir, 'test-out') }
34
+
35
+ it "repacks a string" do
36
+ expect(NMatrix::IO::Matlab.repack("hello", :miUINT8, :byte)).to eq("hello")
37
+ end
38
+
39
+ it "creates yale from internal byte-string function" do
40
+ ia = NMatrix::IO::Matlab.repack("\0\1\3\3\4", :miUINT8, :itype)
41
+ ja = NMatrix::IO::Matlab.repack("\0\1\3\0\0\0\0\0\0\0\0", :miUINT8, :itype)
42
+ n = NMatrix.new(:yale, [4,4], :byte, ia, ja, "\2\3\5\4", :byte)
43
+ expect(n[0,0]).to eq(2)
44
+ expect(n[1,1]).to eq(3)
45
+ expect(n[1,3]).to eq(5)
46
+ expect(n[3,0]).to eq(4)
47
+ expect(n[2,2]).to eq(0)
48
+ expect(n[3,3]).to eq(0)
49
+ end
50
+
51
+ it "reads MATLAB .mat file containing a single square sparse matrix" do
52
+ # Note: same matrix as above
53
+ n = NMatrix::IO::Matlab.load_mat("spec/4x4_sparse.mat")
54
+ expect(n[0,0]).to eq(2)
55
+ expect(n[1,1]).to eq(3)
56
+ expect(n[1,3]).to eq(5)
57
+ expect(n[3,0]).to eq(4)
58
+ expect(n[2,2]).to eq(0)
59
+ expect(n[3,3]).to eq(0)
60
+ end
61
+
62
+ it "reads MATLAB .mat file containing a single dense integer matrix" do
63
+ n = NMatrix::IO::Matlab.load_mat("spec/4x5_dense.mat")
64
+ m = NMatrix.new([4,5], [16,17,18,19,20,15,14,13,12,11,6,7,8,9,10,5,4,3,2,1])
65
+ expect(n).to eq(m)
66
+ end
67
+
68
+ it "reads MATLAB .mat file containing a single dense double matrix" do
69
+ n = NMatrix::IO::Matlab.load_mat("spec/2x2_dense_double.mat")
70
+ m = NMatrix.new(2, [1.1, 2.0, 3.0, 4.0], dtype: :float64)
71
+ expect(n).to eq(m)
72
+ end
73
+
74
+ it "loads and saves MatrixMarket .mtx file containing a single large sparse double matrix" do
75
+ pending "spec disabled because it's so slow"
76
+ n = NMatrix::IO::Market.load("spec/utm5940.mtx")
77
+ NMatrix::IO::Market.save(n, "spec/utm5940.saved.mtx")
78
+ expect(`wc -l spec/utm5940.mtx`.split[0]).to eq(`wc -l spec/utm5940.saved.mtx`.split[0])
79
+ end
80
+
81
+ it "loads a Point Cloud Library PCD file" do
82
+ n = NMatrix::IO::PointCloud.load("spec/test.pcd")
83
+ expect(n.column(0).sort.uniq.size).to eq(1)
84
+ expect(n.column(0).sort.uniq.first).to eq(207.008)
85
+ expect(n[0,3]).to eq(0)
86
+ end
87
+
88
+ it "raises an error when reading a non-existent file" do
89
+ fn = rand(10000000).to_i.to_s
90
+ while File.exist?(fn)
91
+ fn = rand(10000000).to_i.to_s
92
+ end
93
+ expect{ NMatrix.read(fn) }.to raise_error(Errno::ENOENT)
94
+ end
95
+
96
+ it "reads and writes NMatrix dense" do
97
+ n = NMatrix.new(:dense, [4,3], [0,1,2,3,4,5,6,7,8,9,10,11], :int32)
98
+ n.write(test_out)
99
+
100
+ m = NMatrix.read(test_out)
101
+ expect(n).to eq(m)
102
+ end
103
+
104
+ it "reads and writes NMatrix dense as symmetric" do
105
+ n = NMatrix.new(:dense, 3, [0,1,2,1,3,4,2,4,5], :int16)
106
+ n.write(test_out, :symmetric)
107
+
108
+ m = NMatrix.read(test_out)
109
+ expect(n).to eq(m)
110
+ end
111
+
112
+ it "reads and writes NMatrix dense as skew" do
113
+ n = NMatrix.new(:dense, 3, [0,1,2,-1,3,4,-2,-4,5], :float64)
114
+ n.write(test_out, :skew)
115
+
116
+ m = NMatrix.read(test_out)
117
+ expect(n).to eq(m)
118
+ end
119
+
120
+ it "reads and writes NMatrix dense as hermitian" do
121
+ n = NMatrix.new(:dense, 3, [0,1,2,1,3,4,2,4,5], :complex64)
122
+ n.write(test_out, :hermitian)
123
+
124
+ m = NMatrix.read(test_out)
125
+ expect(n).to eq(m)
126
+ end
127
+
128
+ it "reads and writes NMatrix dense as upper" do
129
+ n = NMatrix.new(:dense, 3, [-1,1,2,3,4,5,6,7,8], :int32)
130
+ n.write(test_out, :upper)
131
+
132
+ m = NMatrix.new(:dense, 3, [-1,1,2,0,4,5,0,0,8], :int32) # lower version of the same
133
+
134
+ o = NMatrix.read(test_out)
135
+ expect(o).to eq(m)
136
+ expect(o).not_to eq(n)
137
+ end
138
+
139
+ it "reads and writes NMatrix dense as lower" do
140
+ n = NMatrix.new(:dense, 3, [-1,1,2,3,4,5,6,7,8], :int32)
141
+ n.write(test_out, :lower)
142
+
143
+ m = NMatrix.new(:dense, 3, [-1,0,0,3,4,0,6,7,8], :int32) # lower version of the same
144
+
145
+ o = NMatrix.read(test_out)
146
+ expect(o).to eq(m)
147
+ expect(o).not_to eq(n)
148
+ end
149
+ end
@@ -0,0 +1,482 @@
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
+ # == lapack_core_spec.rb
24
+ #
25
+ # Tests for LAPACK functions that have internal implementations (i.e. they
26
+ # don't rely on external libraries) and also functions that are implemented
27
+ # by both nmatrix-atlas and nmatrix-lapacke. These tests will also be run for the
28
+ # plugins that do use external libraries, since they will override the
29
+ # internal implmentations.
30
+ #
31
+
32
+ require 'spec_helper'
33
+
34
+ describe "NMatrix::LAPACK functions with internal implementations" do
35
+ # where integer math is allowed
36
+ [:byte, :int8, :int16, :int32, :int64, :float32, :float64, :complex64, :complex128].each do |dtype|
37
+ context dtype do
38
+ # This spec seems a little weird. It looks like laswp ignores the last
39
+ # element of piv, though maybe I misunderstand smth. It would make
40
+ # more sense if piv were [2,1,3,3]
41
+ it "exposes clapack laswp" do
42
+ a = NMatrix.new(:dense, [3,4], [1,2,3,4,5,6,7,8,9,10,11,12], dtype)
43
+ NMatrix::LAPACK::clapack_laswp(3, a, 4, 0, 3, [2,1,3,0], 1)
44
+ b = NMatrix.new(:dense, [3,4], [3,2,4,1,7,6,8,5,11,10,12,9], dtype)
45
+ expect(a).to eq(b)
46
+ end
47
+
48
+ # This spec is OK, because the default behavior for permute_columns
49
+ # is :intuitive, which is different from :lapack (default laswp behavior)
50
+ it "exposes NMatrix#permute_columns and #permute_columns! (user-friendly laswp)" do
51
+ a = NMatrix.new(:dense, [3,4], [1,2,3,4,5,6,7,8,9,10,11,12], dtype)
52
+ b = NMatrix.new(:dense, [3,4], [3,2,4,1,7,6,8,5,11,10,12,9], dtype)
53
+ piv = [2,1,3,0]
54
+ r = a.permute_columns(piv)
55
+ expect(r).not_to eq(a)
56
+ expect(r).to eq(b)
57
+ a.permute_columns!(piv)
58
+ expect(a).to eq(b)
59
+ end
60
+ end
61
+ end
62
+
63
+ # where integer math is not allowed
64
+ [:float32, :float64, :complex64, :complex128].each do |dtype|
65
+ context dtype do
66
+
67
+ # clapack_getrf performs a LU decomposition, but unlike the
68
+ # standard LAPACK getrf, it's the upper matrix that has unit diagonals
69
+ # and the permutation is done in columns not rows. See the code for
70
+ # details.
71
+ # Also the rows in the pivot vector are indexed starting from 0,
72
+ # rather than 1 as in LAPACK
73
+ it "calculates LU decomposition using clapack_getrf (row-major, square)" do
74
+ a = NMatrix.new(3, [4,9,2,3,5,7,8,1,6], dtype: dtype)
75
+ ipiv = NMatrix::LAPACK::clapack_getrf(:row, a.shape[0], a.shape[1], a, a.shape[1])
76
+ b = NMatrix.new(3,[9, 2.0/9, 4.0/9,
77
+ 5, 53.0/9, 7.0/53,
78
+ 1, 52.0/9, 360.0/53], dtype: dtype)
79
+ ipiv_true = [1,2,2]
80
+
81
+ # delta varies for different dtypes
82
+ err = case dtype
83
+ when :float32, :complex64
84
+ 1e-6
85
+ when :float64, :complex128
86
+ 1e-15
87
+ end
88
+
89
+ expect(a).to be_within(err).of(b)
90
+ expect(ipiv).to eq(ipiv_true)
91
+ end
92
+
93
+ it "calculates LU decomposition using clapack_getrf (row-major, rectangular)" do
94
+ a = NMatrix.new([3,4], GETRF_EXAMPLE_ARRAY, dtype: dtype)
95
+ ipiv = NMatrix::LAPACK::clapack_getrf(:row, a.shape[0], a.shape[1], a, a.shape[1])
96
+ #we can't use GETRF_SOLUTION_ARRAY here, because of the different
97
+ #conventions of clapack_getrf
98
+ b = NMatrix.new([3,4],[10.0, -0.1, 0.0, 0.4,
99
+ 3.0, 9.3, 20.0/93, 38.0/93,
100
+ 1.0, 7.1, 602.0/93, 251.0/602], dtype: dtype)
101
+ ipiv_true = [2,2,2]
102
+
103
+ # delta varies for different dtypes
104
+ err = case dtype
105
+ when :float32, :complex64
106
+ 1e-6
107
+ when :float64, :complex128
108
+ 1e-15
109
+ end
110
+
111
+ expect(a).to be_within(err).of(b)
112
+ expect(ipiv).to eq(ipiv_true)
113
+ end
114
+
115
+ #Normally we wouldn't check column-major routines, since all our matrices
116
+ #are row-major, but we use the column-major version in #getrf!, so we
117
+ #want to test it here.
118
+ it "calculates LU decomposition using clapack_getrf (col-major, rectangular)" do
119
+ #this is supposed to represent the 3x2 matrix
120
+ # -1 2
121
+ # 0 3
122
+ # 1 -2
123
+ a = NMatrix.new([1,6], [-1,0,1,2,3,-2], dtype: dtype)
124
+ ipiv = NMatrix::LAPACK::clapack_getrf(:col, 3, 2, a, 3)
125
+ b = NMatrix.new([1,6], [-1,0,-1,2,3,0], dtype: dtype)
126
+ ipiv_true = [0,1]
127
+
128
+ # delta varies for different dtypes
129
+ err = case dtype
130
+ when :float32, :complex64
131
+ 1e-6
132
+ when :float64, :complex128
133
+ 1e-15
134
+ end
135
+
136
+ expect(a).to be_within(err).of(b)
137
+ expect(ipiv).to eq(ipiv_true)
138
+ end
139
+
140
+ it "calculates LU decomposition using #getrf! (rectangular)" do
141
+ a = NMatrix.new([3,4], GETRF_EXAMPLE_ARRAY, dtype: dtype)
142
+ ipiv = a.getrf!
143
+ b = NMatrix.new([3,4], GETRF_SOLUTION_ARRAY, dtype: dtype)
144
+ ipiv_true = [2,3,3]
145
+
146
+ # delta varies for different dtypes
147
+ err = case dtype
148
+ when :float32, :complex64
149
+ 1e-6
150
+ when :float64, :complex128
151
+ 1e-14
152
+ end
153
+
154
+ expect(a).to be_within(err).of(b)
155
+ expect(ipiv).to eq(ipiv_true)
156
+ end
157
+
158
+ it "calculates LU decomposition using #getrf! (square)" do
159
+ a = NMatrix.new([4,4], [0,1,2,3, 1,1,1,1, 0,-1,-2,0, 0,2,0,2], dtype: dtype)
160
+ ipiv = a.getrf!
161
+
162
+ b = NMatrix.new([4,4], [1,1,1,1, 0,2,0,2, 0,-0.5,-2,1, 0,0.5,-1,3], dtype: dtype)
163
+ ipiv_true = [2,4,3,4]
164
+
165
+ expect(a).to eq(b)
166
+ expect(ipiv).to eq(ipiv_true)
167
+ end
168
+
169
+ # Together, these calls are basically xGESV from LAPACK: http://www.netlib.org/lapack/double/dgesv.f
170
+ it "exposes clapack_getrs" do
171
+ a = NMatrix.new(3, [-2,4,-3, 3,-2,1, 0,-4,3], dtype: dtype)
172
+ ipiv = NMatrix::LAPACK::clapack_getrf(:row, 3, 3, a, 3)
173
+ b = NMatrix.new([3,1], [-1, 17, -9], dtype: dtype)
174
+
175
+ NMatrix::LAPACK::clapack_getrs(:row, false, 3, 1, a, 3, ipiv, b, 3)
176
+
177
+ expect(b[0]).to eq(5)
178
+ expect(b[1]).to eq(-15.0/2)
179
+ expect(b[2]).to eq(-13)
180
+ end
181
+
182
+ it "solves matrix equation (non-vector rhs) using clapack_getrs" do
183
+ a = NMatrix.new(3, [-2,4,-3, 3,-2,1, 0,-4,3], dtype: dtype)
184
+ b = NMatrix.new([3,2], [-1,2, 17,1, -9,-4], dtype: dtype)
185
+
186
+ n = a.shape[0]
187
+ nrhs = b.shape[1]
188
+
189
+ ipiv = NMatrix::LAPACK::clapack_getrf(:row, n, n, a, n)
190
+ # Even though we pass :row to clapack_getrs, it still interprets b as
191
+ # column-major, so need to transpose b before and after:
192
+ b = b.transpose
193
+ NMatrix::LAPACK::clapack_getrs(:row, false, n, nrhs, a, n, ipiv, b, n)
194
+ b = b.transpose
195
+
196
+ b_true = NMatrix.new([3,2], [5,1, -7.5,1, -13,0], dtype: dtype)
197
+ expect(b).to eq(b_true)
198
+ end
199
+
200
+ #posv is like potrf+potrs
201
+ #posv is implemented in both nmatrix-atlas and nmatrix-lapacke, so the spec
202
+ #needs to be shared here
203
+ it "solves a (symmetric positive-definite) matrix equation using posv (vector rhs)" do
204
+ a = NMatrix.new(3, [4, 0,-1,
205
+ 0, 2, 1,
206
+ 0, 0, 1], dtype: dtype)
207
+ b = NMatrix.new([3,1], [4,2,0], dtype: dtype)
208
+
209
+ begin
210
+ x = NMatrix::LAPACK::posv(:upper, a, b)
211
+ rescue NotImplementedError => e
212
+ pending e.to_s
213
+ end
214
+
215
+ x_true = NMatrix.new([3,1], [1, 1, 0], dtype: dtype)
216
+
217
+ err = case dtype
218
+ when :float32, :complex64
219
+ 1e-5
220
+ when :float64, :complex128
221
+ 1e-14
222
+ end
223
+
224
+ expect(x).to be_within(err).of(x_true)
225
+ end
226
+
227
+ it "solves a (symmetric positive-definite) matrix equation using posv (non-vector rhs)" do
228
+ a = NMatrix.new(3, [4, 0,-1,
229
+ 0, 2, 1,
230
+ 0, 0, 1], dtype: dtype)
231
+ b = NMatrix.new([3,2], [4,-1, 2,-1, 0,0], dtype: dtype)
232
+
233
+ begin
234
+ x = NMatrix::LAPACK::posv(:upper, a, b)
235
+ rescue NotImplementedError => e
236
+ pending e.to_s
237
+ end
238
+
239
+ x_true = NMatrix.new([3,2], [1,0, 1,-1, 0,1], dtype: dtype)
240
+
241
+ err = case dtype
242
+ when :float32, :complex64
243
+ 1e-5
244
+ when :float64, :complex128
245
+ 1e-14
246
+ end
247
+
248
+ expect(x).to be_within(err).of(x_true)
249
+ end
250
+
251
+ it "calculates the singular value decomposition with NMatrix#gesvd" do
252
+ #example from Wikipedia
253
+ m = 4
254
+ n = 5
255
+ mn_min = [m,n].min
256
+ a = NMatrix.new([m,n],[1,0,0,0,2, 0,0,3,0,0, 0,0,0,0,0, 0,4,0,0,0], dtype: dtype)
257
+
258
+ begin
259
+ u, s, vt = a.gesvd
260
+ rescue NotImplementedError => e
261
+ pending e.to_s
262
+ end
263
+
264
+ s_true = NMatrix.new([mn_min,1], [4,3,Math.sqrt(5),0], dtype: a.abs_dtype)
265
+ u_true = NMatrix.new([m,m], [0,0,1,0, 0,1,0,0, 0,0,0,-1, 1,0,0,0], dtype: dtype)
266
+ vt_true = NMatrix.new([n,n], [0,1,0,0,0, 0,0,1,0,0, Math.sqrt(0.2),0,0,0,Math.sqrt(0.8), 0,0,0,1,0, -Math.sqrt(0.8),0,0,0,Math.sqrt(0.2)], dtype: dtype)
267
+
268
+ err = case dtype
269
+ when :float32, :complex64
270
+ 1e-5
271
+ when :float64, :complex128
272
+ 1e-14
273
+ end
274
+
275
+ expect(s).to be_within(err).of(s_true)
276
+ expect(u).to be_within(err).of(u_true)
277
+ expect(vt).to be_within(err).of(vt_true)
278
+
279
+ expect(s.dtype).to eq(a.abs_dtype)
280
+ expect(u.dtype).to eq(dtype)
281
+ expect(vt.dtype).to eq(dtype)
282
+ end
283
+
284
+ it "calculates the singular value decomposition with NMatrix#gesdd" do
285
+ #example from Wikipedia
286
+ m = 4
287
+ n = 5
288
+ mn_min = [m,n].min
289
+ a = NMatrix.new([m,n],[1,0,0,0,2, 0,0,3,0,0, 0,0,0,0,0, 0,4,0,0,0], dtype: dtype)
290
+
291
+ begin
292
+ u, s, vt = a.gesdd
293
+ rescue NotImplementedError => e
294
+ pending e.to_s
295
+ end
296
+
297
+ s_true = NMatrix.new([mn_min,1], [4,3,Math.sqrt(5),0], dtype: a.abs_dtype)
298
+ u_true = NMatrix.new([m,m], [0,0,1,0, 0,1,0,0, 0,0,0,-1, 1,0,0,0], dtype: dtype)
299
+ vt_true = NMatrix.new([n,n], [0,1,0,0,0, 0,0,1,0,0, Math.sqrt(0.2),0,0,0,Math.sqrt(0.8), 0,0,0,1,0, -Math.sqrt(0.8),0,0,0,Math.sqrt(0.2)], dtype: dtype)
300
+
301
+ err = case dtype
302
+ when :float32, :complex64
303
+ 1e-5
304
+ when :float64, :complex128
305
+ 1e-14
306
+ end
307
+
308
+ expect(s).to be_within(err).of(s_true)
309
+ expect(u).to be_within(err).of(u_true)
310
+ expect(vt).to be_within(err).of(vt_true)
311
+ end
312
+
313
+
314
+ it "calculates eigenvalues and eigenvectors NMatrix::LAPACK.geev (real matrix, complex eigenvalues)" do
315
+ n = 3
316
+ a = NMatrix.new([n,n], [-1,0,0, 0,1,-2, 0,1,-1], dtype: dtype)
317
+
318
+ begin
319
+ eigenvalues, vl, vr = NMatrix::LAPACK.geev(a)
320
+ rescue NotImplementedError => e
321
+ pending e.to_s
322
+ end
323
+
324
+ eigenvalues_true = NMatrix.new([n,1], [Complex(0,1), -Complex(0,1), -1], dtype: NMatrix.upcast(dtype, :complex64))
325
+ vr_true = NMatrix.new([n,n],[0,0,1,
326
+ 2/Math.sqrt(6),2/Math.sqrt(6),0,
327
+ Complex(1,-1)/Math.sqrt(6),Complex(1,1)/Math.sqrt(6),0], dtype: NMatrix.upcast(dtype, :complex64))
328
+ vl_true = NMatrix.new([n,n],[0,0,1,
329
+ Complex(-1,1)/Math.sqrt(6),Complex(-1,-1)/Math.sqrt(6),0,
330
+ 2/Math.sqrt(6),2/Math.sqrt(6),0], dtype: NMatrix.upcast(dtype, :complex64))
331
+
332
+ err = case dtype
333
+ when :float32, :complex64
334
+ 1e-6
335
+ when :float64, :complex128
336
+ 1e-15
337
+ end
338
+
339
+ expect(eigenvalues).to be_within(err).of(eigenvalues_true)
340
+ expect(vr).to be_within(err).of(vr_true)
341
+ expect(vl).to be_within(err).of(vl_true)
342
+
343
+ expect(eigenvalues.dtype).to eq(NMatrix.upcast(dtype, :complex64))
344
+ expect(vr.dtype).to eq(NMatrix.upcast(dtype, :complex64))
345
+ expect(vl.dtype).to eq(NMatrix.upcast(dtype, :complex64))
346
+ end
347
+
348
+ it "calculates eigenvalues and eigenvectors NMatrix::LAPACK.geev (real matrix, real eigenvalues)" do
349
+ n = 3
350
+ a = NMatrix.new([n,n], [2,0,0, 0,3,2, 0,1,2], dtype: dtype)
351
+
352
+ begin
353
+ eigenvalues, vl, vr = NMatrix::LAPACK.geev(a)
354
+ rescue NotImplementedError => e
355
+ pending e.to_s
356
+ end
357
+
358
+ eigenvalues_true = NMatrix.new([n,1], [1, 4, 2], dtype: dtype)
359
+
360
+ # For some reason, some of the eigenvectors have different signs
361
+ # when we use the complex versions of geev. This is totally fine, since
362
+ # they are still normalized eigenvectors even with the sign flipped.
363
+ if a.complex_dtype?
364
+ vr_true = NMatrix.new([n,n],[0,0,1,
365
+ 1/Math.sqrt(2),2/Math.sqrt(5),0,
366
+ -1/Math.sqrt(2),1/Math.sqrt(5),0], dtype: dtype)
367
+ vl_true = NMatrix.new([n,n],[0,0,1,
368
+ -1/Math.sqrt(5),1/Math.sqrt(2),0,
369
+ 2/Math.sqrt(5),1/Math.sqrt(2),0], dtype: dtype)
370
+ else
371
+ vr_true = NMatrix.new([n,n],[0,0,1,
372
+ 1/Math.sqrt(2),-2/Math.sqrt(5),0,
373
+ -1/Math.sqrt(2),-1/Math.sqrt(5),0], dtype: dtype)
374
+ vl_true = NMatrix.new([n,n],[0,0,1,
375
+ 1/Math.sqrt(5),-1/Math.sqrt(2),0,
376
+ -2/Math.sqrt(5),-1/Math.sqrt(2),0], dtype: dtype)
377
+ end
378
+
379
+ err = case dtype
380
+ when :float32, :complex64
381
+ 1e-6
382
+ when :float64, :complex128
383
+ 1e-15
384
+ end
385
+
386
+ expect(eigenvalues).to be_within(err).of(eigenvalues_true)
387
+ expect(vr).to be_within(err).of(vr_true)
388
+ expect(vl).to be_within(err).of(vl_true)
389
+
390
+ expect(eigenvalues.dtype).to eq(dtype)
391
+ expect(vr.dtype).to eq(dtype)
392
+ expect(vl.dtype).to eq(dtype)
393
+ end
394
+
395
+ it "calculates eigenvalues and eigenvectors NMatrix::LAPACK.geev (left eigenvectors only)" do
396
+ n = 3
397
+ a = NMatrix.new([n,n], [-1,0,0, 0,1,-2, 0,1,-1], dtype: dtype)
398
+
399
+ begin
400
+ eigenvalues, vl = NMatrix::LAPACK.geev(a, :left)
401
+ rescue NotImplementedError => e
402
+ pending e.to_s
403
+ end
404
+
405
+ eigenvalues_true = NMatrix.new([n,1], [Complex(0,1), -Complex(0,1), -1], dtype: NMatrix.upcast(dtype, :complex64))
406
+ vl_true = NMatrix.new([n,n],[0,0,1,
407
+ Complex(-1,1)/Math.sqrt(6),Complex(-1,-1)/Math.sqrt(6),0,
408
+ 2/Math.sqrt(6),2/Math.sqrt(6),0], dtype: NMatrix.upcast(dtype, :complex64))
409
+
410
+ err = case dtype
411
+ when :float32, :complex64
412
+ 1e-6
413
+ when :float64, :complex128
414
+ 1e-15
415
+ end
416
+
417
+ expect(eigenvalues).to be_within(err).of(eigenvalues_true)
418
+ expect(vl).to be_within(err).of(vl_true)
419
+ end
420
+
421
+ it "calculates eigenvalues and eigenvectors NMatrix::LAPACK.geev (right eigenvectors only)" do
422
+ n = 3
423
+ a = NMatrix.new([n,n], [-1,0,0, 0,1,-2, 0,1,-1], dtype: dtype)
424
+
425
+ begin
426
+ eigenvalues, vr = NMatrix::LAPACK.geev(a, :right)
427
+ rescue NotImplementedError => e
428
+ pending e.to_s
429
+ end
430
+
431
+ eigenvalues_true = NMatrix.new([n,1], [Complex(0,1), -Complex(0,1), -1], dtype: NMatrix.upcast(dtype, :complex64))
432
+ vr_true = NMatrix.new([n,n],[0,0,1,
433
+ 2/Math.sqrt(6),2/Math.sqrt(6),0,
434
+ Complex(1,-1)/Math.sqrt(6),Complex(1,1)/Math.sqrt(6),0], dtype: NMatrix.upcast(dtype, :complex64))
435
+
436
+ err = case dtype
437
+ when :float32, :complex64
438
+ 1e-6
439
+ when :float64, :complex128
440
+ 1e-15
441
+ end
442
+
443
+ expect(eigenvalues).to be_within(err).of(eigenvalues_true)
444
+ expect(vr).to be_within(err).of(vr_true)
445
+ end
446
+ end
447
+ end
448
+
449
+ [:complex64, :complex128].each do |dtype|
450
+ context dtype do
451
+ it "calculates eigenvalues and eigenvectors NMatrix::LAPACK.geev (complex matrix)" do
452
+ n = 3
453
+ a = NMatrix.new([n,n], [Complex(0,1),0,0, 0,3,2, 0,1,2], dtype: dtype)
454
+
455
+ begin
456
+ eigenvalues, vl, vr = NMatrix::LAPACK.geev(a)
457
+ rescue NotImplementedError => e
458
+ pending e.to_s
459
+ end
460
+
461
+ eigenvalues_true = NMatrix.new([n,1], [1, 4, Complex(0,1)], dtype: dtype)
462
+ vr_true = NMatrix.new([n,n],[0,0,1,
463
+ 1/Math.sqrt(2),2/Math.sqrt(5),0,
464
+ -1/Math.sqrt(2),1/Math.sqrt(5),0], dtype: dtype)
465
+ vl_true = NMatrix.new([n,n],[0,0,1,
466
+ -1/Math.sqrt(5),1/Math.sqrt(2),0,
467
+ 2/Math.sqrt(5),1/Math.sqrt(2),0], dtype: dtype)
468
+
469
+ err = case dtype
470
+ when :float32, :complex64
471
+ 1e-6
472
+ when :float64, :complex128
473
+ 1e-15
474
+ end
475
+
476
+ expect(eigenvalues).to be_within(err).of(eigenvalues_true)
477
+ expect(vr).to be_within(err).of(vr_true)
478
+ expect(vl).to be_within(err).of(vl_true)
479
+ end
480
+ end
481
+ end
482
+ end