numo-linalg 0.1.2 → 0.1.3

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -3
  3. data/ext/numo/linalg/blas/extconf.rb +1 -2
  4. data/ext/numo/linalg/blas/numo_blas.h +6 -0
  5. data/ext/numo/linalg/blas/tmpl/mv.c +3 -2
  6. data/ext/numo/linalg/lapack/gen/spec.rb +5 -0
  7. data/ext/numo/linalg/lapack/lapack.c +29 -0
  8. data/ext/numo/linalg/lapack/numo_lapack.h +3 -0
  9. data/ext/numo/linalg/lapack/tmpl/gqr.c +1 -1
  10. data/ext/numo/linalg/lapack/tmpl/sygvx.c +130 -0
  11. data/ext/numo/linalg/mkmf_linalg.rb +2 -19
  12. data/lib/numo/linalg/function.rb +168 -77
  13. data/lib/numo/linalg/loader.rb +6 -14
  14. data/lib/numo/linalg/version.rb +1 -1
  15. data/numo-linalg.gemspec +2 -1
  16. data/spec/linalg/autoloader_spec.rb +27 -0
  17. data/spec/linalg/function/cho_fact_spec.rb +31 -0
  18. data/spec/linalg/function/cho_inv_spec.rb +39 -0
  19. data/spec/linalg/function/cho_solve_spec.rb +66 -0
  20. data/spec/linalg/function/cholesky_spec.rb +43 -0
  21. data/spec/linalg/function/cond_spec.rb +57 -0
  22. data/spec/linalg/function/det_spec.rb +21 -0
  23. data/spec/linalg/function/dot_spec.rb +84 -0
  24. data/spec/linalg/function/eig_spec.rb +53 -0
  25. data/spec/linalg/function/eigh_spec.rb +81 -0
  26. data/spec/linalg/function/eigvals_spec.rb +27 -0
  27. data/spec/linalg/function/eigvalsh_spec.rb +60 -0
  28. data/spec/linalg/function/inv_spec.rb +57 -0
  29. data/spec/linalg/function/ldl_spec.rb +51 -0
  30. data/spec/linalg/function/lstsq_spec.rb +80 -0
  31. data/spec/linalg/function/lu_fact_spec.rb +34 -0
  32. data/spec/linalg/function/lu_inv_spec.rb +21 -0
  33. data/spec/linalg/function/lu_solve_spec.rb +40 -0
  34. data/spec/linalg/function/lu_spec.rb +46 -0
  35. data/spec/linalg/function/matmul_spec.rb +41 -0
  36. data/spec/linalg/function/matrix_power_spec.rb +31 -0
  37. data/spec/linalg/function/matrix_rank_spec.rb +33 -0
  38. data/spec/linalg/function/norm_spec.rb +81 -0
  39. data/spec/linalg/function/pinv_spec.rb +48 -0
  40. data/spec/linalg/function/qr_spec.rb +82 -0
  41. data/spec/linalg/function/slogdet_spec.rb +21 -0
  42. data/spec/linalg/function/solve_spec.rb +98 -0
  43. data/spec/linalg/function/svd_spec.rb +88 -0
  44. data/spec/linalg/function/svdvals_spec.rb +40 -0
  45. data/spec/spec_helper.rb +55 -0
  46. metadata +79 -6
  47. data/spec/lapack_spec.rb +0 -13
@@ -16,24 +16,16 @@ module Numo
16
16
  if dir && !dir.empty?
17
17
  base = File.join(dir,base)
18
18
  end
19
- path = base+".#{EXT}"
20
- if NEED_VERSION_SUFFIX
21
- if ver
22
- path += ".#{ver}"
23
- end
24
- recvr.send(:dlopen,path)
25
- else
19
+ if ver && EXT=='so'
26
20
  begin
21
+ path = "#{base}.#{EXT}.#{ver}"
27
22
  recvr.send(:dlopen,path)
28
- rescue => e
29
- if ver
30
- path += ".#{ver}"
31
- recvr.send(:dlopen,path)
32
- else
33
- raise e
34
- end
23
+ return path
24
+ rescue
35
25
  end
36
26
  end
27
+ path = "#{base}.#{EXT}"
28
+ recvr.send(:dlopen,path)
37
29
  path
38
30
  end
39
31
  private :dlopen
@@ -1,5 +1,5 @@
1
1
  module Numo
2
2
  module Linalg
3
- VERSION = "0.1.2"
3
+ VERSION = "0.1.3"
4
4
  end
5
5
  end
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
21
21
  "ext/numo/linalg/lapack/extconf.rb"]
22
22
 
23
23
  spec.add_development_dependency "bundler", "~> 1.3"
24
- spec.add_development_dependency "rake", "~> 0"
24
+ spec.add_development_dependency "rake", "~> 12.0"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
25
26
  spec.add_runtime_dependency "numo-narray", ">= 0.9.0.7"
26
27
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg::Autoloader do
6
+ it 'succeses loading backend libraries' do
7
+ expect { described_class.load_library }.to_not raise_error
8
+ expect(described_class.libs).to_not be_nil
9
+ end
10
+
11
+ describe 'private method' do
12
+ let(:autoloader) do
13
+ class DummyLoader
14
+ include Numo::Linalg::Autoloader
15
+ end
16
+ DummyLoader.new
17
+ end
18
+
19
+ it 'finds BLAS/LAPACK libraries' do
20
+ lapack_libs = autoloader.send(:find_lapack_libs, ['/usr/lib', '/usr/local/lib', '/usr/local/opt/lapack/lib'])
21
+ expect(lapack_libs[:blas]).to_not be_nil
22
+ expect(lapack_libs[:cblas]).to_not be_nil
23
+ expect(lapack_libs[:lapack]).to_not be_nil
24
+ expect(lapack_libs[:lapacke]).to_not be_nil
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'cho_fact' do
7
+ let(:m) { 5 }
8
+ let(:mat_a) do
9
+ a = rand_symmetric_mat(m)
10
+ a.dot(a.transpose)
11
+ end
12
+ let(:mat_b) do
13
+ b = rand_hermitian_mat(m)
14
+ b.dot(b.transpose.conj)
15
+ end
16
+
17
+ it 'calculates the cholesky factorization of a symmetric positive-definite matrix' do
18
+ mat_u = described_class.cho_fact(mat_a, uplo: 'U').triu
19
+ expect((mat_a - mat_u.transpose.dot(mat_u)).abs.max).to be < ERR_TOL
20
+ # mat_l = described_class.cho_fact(mat_a, uplo: 'L').tril
21
+ # expect((mat_a - mat_l.dot(mat_l.transpose)).abs.max).to be < ERR_TOL
22
+ end
23
+
24
+ it 'calculates the cholesky factorization of a hermitian positive-definite matrix' do
25
+ mat_u = described_class.cho_fact(mat_b, uplo: 'U').triu
26
+ expect((mat_b - mat_u.transpose.conj.dot(mat_u)).abs.max).to be < ERR_TOL
27
+ # mat_l = described_class.cho_fact(mat_b, uplo: 'L').tril
28
+ # expect((mat_b - mat_l.dot(mat_l.transpose)).abs.max).to be < ERR_TOL
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'cho_inv' do
7
+ let(:m) { 5 }
8
+ let(:mat_a) do
9
+ a = rand_symmetric_mat(m)
10
+ a.dot(a.transpose)
11
+ end
12
+ let(:mat_b) do
13
+ b = rand_hermitian_mat(m)
14
+ b.dot(b.transpose.conj)
15
+ end
16
+
17
+ it 'calculates the inverse of a symmetric positive-definite matrix' do
18
+ mat_u = described_class.cho_fact(mat_a, uplo: 'U').triu
19
+ tri_inv_mat_a = described_class.cho_inv(mat_u, uplo: 'U')
20
+ inv_mat_a = tri_inv_mat_a + tri_inv_mat_a.transpose - tri_inv_mat_a.diagonal.diag
21
+ expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
22
+ # mat_l = described_class.cho_fact(mat_a, uplo: 'L').tril
23
+ # tri_inv_mat_a = described_class.cho_inv(mat_l, uplo: 'L')
24
+ # inv_mat_a = tri_inv_mat_a + tri_inv_mat_a.transpose - tri_inv_mat_a.diagonal.diag
25
+ # expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
26
+ end
27
+
28
+ it 'calculates the inverse of a hermitian positive-definite matrix' do
29
+ mat_u = described_class.cho_fact(mat_b, uplo: 'U').triu
30
+ tri_inv_mat_b = described_class.cho_inv(mat_u, uplo: 'U')
31
+ inv_mat_b = tri_inv_mat_b + tri_inv_mat_b.transpose.conj - tri_inv_mat_b.diagonal.diag
32
+ expect((inv_mat_b.dot(mat_b) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
33
+ # mat_l = described_class.cho_fact(mat_b, uplo: 'L').tril
34
+ # tri_inv_mat_b = described_class.cho_inv(mat_l, uplo: 'L')
35
+ # inv_mat_b = tri_inv_mat_b + tri_inv_mat_b.transpose.conj - tri_inv_mat_b.diagonal.diag
36
+ # expect((inv_mat_b.dot(mat_b) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'cho_solve' do
7
+ let(:m) { 5 }
8
+ let(:n) { 3 }
9
+ let(:mat_a) do
10
+ a = rand_symmetric_mat(m)
11
+ a.dot(a.transpose)
12
+ end
13
+ let(:mat_h) do
14
+ b = rand_hermitian_mat(m)
15
+ b.dot(b.transpose.conj)
16
+ end
17
+ let(:mat_b) { rand_rect_real_mat(m, n) }
18
+ let(:vec_b) { rand_real_vec(m) }
19
+ let(:mat_c) { rand_square_complex_mat(m) }
20
+ let(:mat_d) { rand_rect_complex_mat(m, n) }
21
+ let(:vec_d) { rand_complex_vec(m) }
22
+
23
+
24
+ it 'solves the linear equation A x = b with a symmetric positive-definite matrix A' do
25
+ mat_u = described_class.cho_fact(mat_a, uplo: 'U').triu
26
+ vec_x = described_class.cho_solve(mat_u, vec_b, uplo: 'U')
27
+ expect((mat_a.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
28
+ mat_x = described_class.cho_solve(mat_u, mat_b, uplo: 'U')
29
+ expect((mat_a.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
30
+ vec_x = described_class.cho_solve(mat_u, vec_d, uplo: 'U')
31
+ expect((mat_a.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
32
+ mat_x = described_class.cho_solve(mat_u, mat_d, uplo: 'U')
33
+ expect((mat_a.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
34
+ # mat_l = described_class.cho_fact(mat_a, uplo: 'L').tril
35
+ # vec_x = described_class.cho_solve(mat_l, vec_b, uplo: 'L')
36
+ # expect((mat_a.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
37
+ # mat_x = described_class.cho_solve(mat_l, mat_b, uplo: 'L')
38
+ # expect((mat_a.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
39
+ # vec_x = described_class.cho_solve(mat_l, vec_d, uplo: 'L')
40
+ # expect((mat_a.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
41
+ # mat_x = described_class.cho_solve(mat_l, mat_d, uplo: 'L')
42
+ # expect((mat_a.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
43
+ end
44
+
45
+ it 'solves the linear equation A x = b with a hermitian positive-definite matrix' do
46
+ mat_u = described_class.cho_fact(mat_h, uplo: 'U').triu
47
+ vec_x = described_class.cho_solve(mat_u, vec_b, uplo: 'U')
48
+ expect((mat_h.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
49
+ mat_x = described_class.cho_solve(mat_u, mat_b, uplo: 'U')
50
+ expect((mat_h.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
51
+ vec_x = described_class.cho_solve(mat_u, vec_d, uplo: 'U')
52
+ expect((mat_h.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
53
+ mat_x = described_class.cho_solve(mat_u, mat_d, uplo: 'U')
54
+ expect((mat_h.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
55
+ # mat_l = described_class.cho_fact(mat_h, uplo: 'L').tril
56
+ # vec_x = described_class.cho_solve(mat_l, vec_b, uplo: 'L')
57
+ # expect((mat_h.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
58
+ # mat_x = described_class.cho_solve(mat_l, mat_b, uplo: 'L')
59
+ # expect((mat_h.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
60
+ # vec_x = described_class.cho_solve(mat_l, vec_d, uplo: 'L')
61
+ # expect((mat_h.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
62
+ # mat_x = described_class.cho_solve(mat_l, mat_d, uplo: 'L')
63
+ # expect((mat_h.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'cholesky' do
7
+ let(:m) { 5 }
8
+ let(:mat_a) do
9
+ a = rand_symmetric_mat(m)
10
+ a.dot(a.transpose)
11
+ end
12
+ let(:mat_b) do
13
+ b = rand_hermitian_mat(m)
14
+ b.dot(b.transpose.conj)
15
+ end
16
+
17
+ it 'raises ShapeError given a vector' do
18
+ expect { described_class.cholesky(Numo::DFloat.new(3).rand) }.to raise_error(Numo::NArray::ShapeError)
19
+ end
20
+
21
+ it 'raises ShapeError given a rectangular matrix' do
22
+ expect { described_class.cholesky(Numo::DFloat.new(2, 4).rand) }.to raise_error(Numo::NArray::ShapeError)
23
+ end
24
+
25
+ it 'raises ArgumentError given an invalid uplo option' do
26
+ expect { described_class.cholesky(mat_a, uplo: 'A') }.to raise_error(ArgumentError)
27
+ end
28
+
29
+ it 'calculates the cholesky factorization of a symmetric positive-definite matrix' do
30
+ mat_u = described_class.cholesky(mat_a, uplo: 'U')
31
+ expect((mat_a - mat_u.transpose.dot(mat_u)).abs.max).to be < ERR_TOL
32
+ mat_l = described_class.cholesky(mat_a, uplo: 'L')
33
+ expect((mat_a - mat_l.dot(mat_l.transpose)).abs.max).to be < ERR_TOL
34
+ end
35
+
36
+ it 'calculates the cholesky factorization of a hermitian positive-definite matrix' do
37
+ mat_u = described_class.cholesky(mat_b, uplo: 'U')
38
+ expect((mat_b - mat_u.transpose.conj.dot(mat_u)).abs.max).to be < ERR_TOL
39
+ mat_l = described_class.cholesky(mat_b, uplo: 'L')
40
+ expect((mat_b - mat_l.dot(mat_l.transpose.conj)).abs.max).to be < ERR_TOL
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'cond' do
7
+ let(:m) { 6 }
8
+ let(:n) { 3 }
9
+ let(:mat_a) { rand_square_real_mat(m) }
10
+ let(:mat_b) { rand_square_complex_mat(m) }
11
+
12
+ def cond(mat, ord)
13
+ described_class.norm(mat, ord) * described_class.norm(described_class.inv(mat), ord)
14
+ end
15
+
16
+ it 'raises ArgumentError given an invalid order option' do
17
+ expect { described_class.cond(mat_a, 0) }.to raise_error(ArgumentError)
18
+ expect { described_class.cond(mat_a, 'frobenius') }.to raise_error(ArgumentError)
19
+ end
20
+
21
+ it 'calculates the condition number of a real matrix with Froubenius norm' do
22
+ expect(described_class.cond(mat_a, 'fro')).to be_within(cond(mat_a, 'fro')).of(ERR_TOL)
23
+ end
24
+
25
+ it 'calculates the condition number of a real matrix with L1 norm' do
26
+ expect(described_class.cond(mat_a, 1)).to be_within(cond(mat_a, 1)).of(ERR_TOL)
27
+ end
28
+
29
+ it 'calculates the condition number of a real matrix with L2 norm' do
30
+ condnum = cond(mat_a, 2)
31
+ expect(described_class.cond(mat_a)).to be_within(condnum).of(ERR_TOL)
32
+ expect(described_class.cond(mat_a, 2)).to be_within(condnum).of(ERR_TOL)
33
+ end
34
+
35
+ it 'calculates the condition number of a real matrix with inifinity norm' do
36
+ expect(described_class.cond(mat_a, 'inf')).to be_within(cond(mat_a, 'inf')).of(ERR_TOL)
37
+ end
38
+
39
+ it 'calculates the condition number of a complex matrix with Froubenius norm' do
40
+ expect(described_class.cond(mat_b, 'fro')).to be_within(cond(mat_b, 'fro')).of(ERR_TOL)
41
+ end
42
+
43
+ it 'calculates the condition number of a complex matrix with L1 norm' do
44
+ expect(described_class.cond(mat_b, 1)).to be_within(cond(mat_b, 1)).of(ERR_TOL)
45
+ end
46
+
47
+ it 'calculates the condition number of a complex matrix with L2 norm' do
48
+ condnum = cond(mat_b, 2)
49
+ expect(described_class.cond(mat_b)).to be_within(condnum).of(ERR_TOL)
50
+ expect(described_class.cond(mat_b, 2)).to be_within(condnum).of(ERR_TOL)
51
+ end
52
+
53
+ it 'calculates the condition number of a complex matrix with inifinity norm' do
54
+ expect(described_class.cond(mat_b, 'inf')).to be_within(cond(mat_b, 'inf')).of(ERR_TOL)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'det' do
7
+ let(:m) { 5 }
8
+ let(:mat_a) { rand_square_real_mat(m) }
9
+ let(:mat_b) { rand_square_complex_mat(m) }
10
+
11
+ it 'calculates the dererminant of a square real matrix' do
12
+ det = described_class.eigvals(mat_a).prod.real
13
+ expect(described_class.det(mat_a)).to be_within(det).of(ERR_TOL)
14
+ end
15
+
16
+ it 'calculates the dererminant of a square complex matrix' do
17
+ det = described_class.eigvals(mat_b).prod
18
+ expect((described_class.det(mat_b) - det).abs).to be < ERR_TOL
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'dot' do
7
+ let(:m) { 5 }
8
+ let(:n) { 3 }
9
+ let(:vec_s) { Numo::SFloat.new(m).rand }
10
+ let(:vec_d) { Numo::DFloat.new(m).rand }
11
+ let(:vec_c) { Numo::SComplex.new(m).rand }
12
+ let(:vec_z) { Numo::DComplex.new(m).rand }
13
+ let(:mat_s) { Numo::SFloat.new(m, n).rand }
14
+ let(:mat_d) { Numo::DFloat.new(m, n).rand }
15
+ let(:mat_c) { Numo::SComplex.new(m, n).rand }
16
+ let(:mat_z) { Numo::DComplex.new(m, n).rand }
17
+
18
+ def dot_vec_vec(a, b)
19
+ Array.new(a.size) { |idx| a[idx] * b[idx] }.reduce(&:+)
20
+ end
21
+
22
+ def dot_mat_vec(a, b)
23
+ n, = a.shape
24
+ Numo::NArray.asarray(Array.new(n) { |idx| dot_vec_vec(a[idx, true], b) })
25
+ end
26
+
27
+ def dot_mat_mat(a, b)
28
+ _m, n = b.shape
29
+ Numo::NArray.asarray(Array.new(n) { |idx| dot_mat_vec(a, b[true, idx]) })
30
+ end
31
+
32
+ it 'calculates the dot product of vectors' do
33
+ expect(described_class.dot(vec_s, vec_s)).to be_within(dot_vec_vec(vec_s, vec_s)).of(ERR_TOL)
34
+ expect(described_class.dot(vec_s, vec_d)).to be_within(dot_vec_vec(vec_s, vec_d)).of(ERR_TOL)
35
+ expect(described_class.dot(vec_d, vec_d)).to be_within(dot_vec_vec(vec_d, vec_d)).of(ERR_TOL)
36
+ expect((described_class.dot(vec_s, vec_c) - dot_vec_vec(vec_s, vec_c)).abs).to be < ERR_TOL
37
+ expect((described_class.dot(vec_s, vec_z) - dot_vec_vec(vec_s, vec_z)).abs).to be < ERR_TOL
38
+ expect((described_class.dot(vec_d, vec_c) - dot_vec_vec(vec_d, vec_c)).abs).to be < ERR_TOL
39
+ expect((described_class.dot(vec_d, vec_z) - dot_vec_vec(vec_d, vec_z)).abs).to be < ERR_TOL
40
+ expect((described_class.dot(vec_c, vec_c) - dot_vec_vec(vec_c, vec_c)).abs).to be < ERR_TOL
41
+ expect((described_class.dot(vec_c, vec_z) - dot_vec_vec(vec_c, vec_z)).abs).to be < ERR_TOL
42
+ expect((described_class.dot(vec_z, vec_z) - dot_vec_vec(vec_z, vec_z)).abs).to be < ERR_TOL
43
+ end
44
+
45
+ it 'calculates the dot product of matrices' do
46
+ expect((described_class.dot(mat_s, mat_s.transpose) - dot_mat_mat(mat_s, mat_s.transpose)).abs).to be < ERR_TOL
47
+ expect((described_class.dot(mat_s, mat_d.transpose) - dot_mat_mat(mat_s, mat_d.transpose)).abs).to be < ERR_TOL
48
+ expect((described_class.dot(mat_s, mat_c.transpose) - dot_mat_mat(mat_s, mat_c.transpose)).abs).to be < ERR_TOL
49
+ expect((described_class.dot(mat_s, mat_z.transpose) - dot_mat_mat(mat_s, mat_z.transpose)).abs).to be < ERR_TOL
50
+ expect((described_class.dot(mat_d, mat_d.transpose) - dot_mat_mat(mat_d, mat_d.transpose)).abs).to be < ERR_TOL
51
+ expect((described_class.dot(mat_d, mat_c.transpose) - dot_mat_mat(mat_d, mat_c.transpose)).abs).to be < ERR_TOL
52
+ expect((described_class.dot(mat_d, mat_z.transpose) - dot_mat_mat(mat_d, mat_z.transpose)).abs).to be < ERR_TOL
53
+ expect((described_class.dot(mat_c, mat_c.transpose) - dot_mat_mat(mat_c, mat_c.transpose)).abs).to be < ERR_TOL
54
+ expect((described_class.dot(mat_c, mat_z.transpose) - dot_mat_mat(mat_c, mat_z.transpose)).abs).to be < ERR_TOL
55
+ expect((described_class.dot(mat_z, mat_z.transpose) - dot_mat_mat(mat_z, mat_z.transpose)).abs).to be < ERR_TOL
56
+ end
57
+
58
+ it 'calculates the dot product of matrix and vector' do
59
+ expect((described_class.dot(mat_s.transpose, vec_s) - dot_mat_vec(mat_s.transpose, vec_s)).abs).to be < ERR_TOL
60
+ expect((described_class.dot(mat_d.transpose, vec_s) - dot_mat_vec(mat_d.transpose, vec_s)).abs).to be < ERR_TOL
61
+ expect((described_class.dot(mat_c.transpose, vec_s) - dot_mat_vec(mat_c.transpose, vec_s)).abs).to be < ERR_TOL
62
+ expect((described_class.dot(mat_z.transpose, vec_s) - dot_mat_vec(mat_z.transpose, vec_s)).abs).to be < ERR_TOL
63
+ expect((described_class.dot(mat_d.transpose, vec_d) - dot_mat_vec(mat_d.transpose, vec_d)).abs).to be < ERR_TOL
64
+ expect((described_class.dot(mat_c.transpose, vec_d) - dot_mat_vec(mat_c.transpose, vec_d)).abs).to be < ERR_TOL
65
+ expect((described_class.dot(mat_z.transpose, vec_d) - dot_mat_vec(mat_z.transpose, vec_d)).abs).to be < ERR_TOL
66
+ expect((described_class.dot(mat_c.transpose, vec_c) - dot_mat_vec(mat_c.transpose, vec_c)).abs).to be < ERR_TOL
67
+ expect((described_class.dot(mat_z.transpose, vec_c) - dot_mat_vec(mat_z.transpose, vec_c)).abs).to be < ERR_TOL
68
+ expect((described_class.dot(mat_z.transpose, vec_z) - dot_mat_vec(mat_z.transpose, vec_z)).abs).to be < ERR_TOL
69
+ end
70
+
71
+ it 'calculates the dot product of vector and matrix' do
72
+ expect((described_class.dot(vec_s, mat_s) - dot_mat_vec(mat_s.transpose, vec_s)).abs).to be < ERR_TOL
73
+ expect((described_class.dot(vec_s, mat_d) - dot_mat_vec(mat_d.transpose, vec_s)).abs).to be < ERR_TOL
74
+ expect((described_class.dot(vec_s, mat_c) - dot_mat_vec(mat_c.transpose, vec_s)).abs).to be < ERR_TOL
75
+ expect((described_class.dot(vec_s, mat_z) - dot_mat_vec(mat_z.transpose, vec_s)).abs).to be < ERR_TOL
76
+ expect((described_class.dot(vec_d, mat_d) - dot_mat_vec(mat_d.transpose, vec_d)).abs).to be < ERR_TOL
77
+ expect((described_class.dot(vec_d, mat_c) - dot_mat_vec(mat_c.transpose, vec_d)).abs).to be < ERR_TOL
78
+ expect((described_class.dot(vec_d, mat_z) - dot_mat_vec(mat_z.transpose, vec_d)).abs).to be < ERR_TOL
79
+ expect((described_class.dot(vec_c, mat_c) - dot_mat_vec(mat_c.transpose, vec_c)).abs).to be < ERR_TOL
80
+ expect((described_class.dot(vec_c, mat_z) - dot_mat_vec(mat_z.transpose, vec_c)).abs).to be < ERR_TOL
81
+ expect((described_class.dot(vec_z, mat_z) - dot_mat_vec(mat_z.transpose, vec_z)).abs).to be < ERR_TOL
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'eig' do
7
+ let(:m) { 5 }
8
+ let(:mat_a) { rand_square_real_mat(m) }
9
+ let(:mat_b) { rand_square_complex_mat(m) }
10
+
11
+ it 'raises ShapeError given a rectangular matrix' do
12
+ expect { described_class.eigh(Numo::DFloat.new(2, 4).rand) }.to raise_error(Numo::NArray::ShapeError)
13
+ end
14
+
15
+ it 'finds eigenvalues and eigenvectors for a square nonsymmetric matrix' do
16
+ w, vl, vr = described_class.eig(mat_a, left: true, right: true)
17
+ expect(vl).not_to be_nil
18
+ expect(vr).not_to be_nil
19
+ expect((mat_a.dot(vr) - vr.dot(w.diag)).abs.max).to be < ERR_TOL
20
+ expect((vl.transpose.conj.dot(mat_a) - w.diag.dot(vl.transpose.conj)).abs.max).to be < ERR_TOL
21
+ end
22
+
23
+ it 'finds eigenvalues and right eigenvectors for a square nonsymmetric matrix' do
24
+ w, vl, vr = described_class.eig(mat_a, left: false, right: true)
25
+ expect(vl).to be_nil
26
+ expect((mat_a.dot(vr) - vr.dot(w.diag)).abs.max).to be < ERR_TOL
27
+ end
28
+
29
+ it 'finds eigenvalues and left eigenvectors for a square nonsymmetric matrix' do
30
+ w, vl, vr = described_class.eig(mat_a, left: true, right: false)
31
+ expect((vl.transpose.conj.dot(mat_a) - w.diag.dot(vl.transpose.conj)).abs.max).to be < ERR_TOL
32
+ end
33
+
34
+ it 'finds eigenvalues and eigenvectors for a square complex nonsymmetric matrix' do
35
+ w, vl, vr = described_class.eig(mat_b, left: true, right: true)
36
+ expect(vl).not_to be_nil
37
+ expect(vr).not_to be_nil
38
+ expect((mat_b.dot(vr) - vr.dot(w.diag)).abs.max).to be < ERR_TOL
39
+ expect((vl.transpose.conj.dot(mat_b) - w.diag.dot(vl.transpose.conj)).abs.max).to be < ERR_TOL
40
+ end
41
+
42
+ it 'finds eigenvalues and right eigenvectors for a square complex nonsymmetric matrix' do
43
+ w, vl, vr = described_class.eig(mat_b, left: false, right: true)
44
+ expect(vl).to be_nil
45
+ expect((mat_b.dot(vr) - vr.dot(w.diag)).abs.max).to be < ERR_TOL
46
+ end
47
+
48
+ it 'finds eigenvalues and left eigenvectors for a square complex nonsymmetric matrix' do
49
+ w, vl, vr = described_class.eig(mat_b, left: true, right: false)
50
+ expect((vl.transpose.conj.dot(mat_b) - w.diag.dot(vl.transpose.conj)).abs.max).to be < ERR_TOL
51
+ end
52
+ end
53
+ end