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
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'lu' do
7
+ let(:m) { 5 }
8
+ let(:n) { 3 }
9
+ let(:mat_a) { rand_rect_real_mat(m, n) }
10
+ let(:mat_b) { rand_rect_real_mat(n, m) }
11
+ let(:mat_c) { rand_rect_complex_mat(m, n) }
12
+ let(:mat_d) { rand_rect_complex_mat(n, m) }
13
+
14
+ it 'raises ShapeError given a vector' do
15
+ expect { described_class.lu(Numo::DFloat.new(3).rand) }.to raise_error(Numo::NArray::ShapeError)
16
+ end
17
+
18
+ it 'calculates the LU factorization of a rectangular real matrix' do
19
+ mat_p, mat_l, mat_u = described_class.lu(mat_a)
20
+ expect((mat_a - mat_p.dot(mat_l.dot(mat_u))).abs.max).to be < ERR_TOL
21
+ mat_p, mat_l, mat_u = described_class.lu(mat_b)
22
+ expect((mat_b - mat_p.dot(mat_l.dot(mat_u))).abs.max).to be < ERR_TOL
23
+ end
24
+
25
+ it 'calculates the LU factorization of a rectangular real matrix including permutation of L' do
26
+ mat_l, mat_u = described_class.lu(mat_a, permute_l: true)
27
+ expect((mat_a - mat_l.dot(mat_u)).abs.max).to be < ERR_TOL
28
+ mat_l, mat_u = described_class.lu(mat_b, permute_l: true)
29
+ expect((mat_b - mat_l.dot(mat_u)).abs.max).to be < ERR_TOL
30
+ end
31
+
32
+ it 'calculates the LU factorization of a rectangular complex matrix' do
33
+ mat_p, mat_l, mat_u = described_class.lu(mat_c)
34
+ expect((mat_c - mat_p.dot(mat_l.dot(mat_u))).abs.max).to be < ERR_TOL
35
+ mat_p, mat_l, mat_u = described_class.lu(mat_d)
36
+ expect((mat_d - mat_p.dot(mat_l.dot(mat_u))).abs.max).to be < ERR_TOL
37
+ end
38
+
39
+ it 'calculates the LU factorization of a rectangular complex matrix including permutation of L' do
40
+ mat_l, mat_u = described_class.lu(mat_c, permute_l: true)
41
+ expect((mat_c - mat_l.dot(mat_u)).abs.max).to be < ERR_TOL
42
+ mat_l, mat_u = described_class.lu(mat_d, permute_l: true)
43
+ expect((mat_d - mat_l.dot(mat_u)).abs.max).to be < ERR_TOL
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'matmul' do
7
+ let(:m) { 5 }
8
+ let(:n) { 3 }
9
+ let(:mat_s) { Numo::SFloat.new(m, n).rand }
10
+ let(:mat_d) { Numo::DFloat.new(m, n).rand }
11
+ let(:mat_c) { Numo::SComplex.new(m, n).rand }
12
+ let(:mat_z) { Numo::DComplex.new(m, n).rand }
13
+
14
+ def dot_vec_vec(a, b)
15
+ Array.new(a.size) { |idx| a[idx] * b[idx] }.reduce(&:+)
16
+ end
17
+
18
+ def dot_mat_vec(a, b)
19
+ n, = a.shape
20
+ Numo::NArray.asarray(Array.new(n) { |idx| dot_vec_vec(a[idx, true], b) })
21
+ end
22
+
23
+ def dot_mat_mat(a, b)
24
+ _m, n = b.shape
25
+ Numo::NArray.asarray(Array.new(n) { |idx| dot_mat_vec(a, b[true, idx]) })
26
+ end
27
+
28
+ it 'calculates the dot product of matrices' do
29
+ expect((described_class.matmul(mat_s, mat_s.transpose) - dot_mat_mat(mat_s, mat_s.transpose)).abs).to be < ERR_TOL
30
+ expect((described_class.matmul(mat_s, mat_d.transpose) - dot_mat_mat(mat_s, mat_d.transpose)).abs).to be < ERR_TOL
31
+ expect((described_class.matmul(mat_s, mat_c.transpose) - dot_mat_mat(mat_s, mat_c.transpose)).abs).to be < ERR_TOL
32
+ expect((described_class.matmul(mat_s, mat_z.transpose) - dot_mat_mat(mat_s, mat_z.transpose)).abs).to be < ERR_TOL
33
+ expect((described_class.matmul(mat_d, mat_d.transpose) - dot_mat_mat(mat_d, mat_d.transpose)).abs).to be < ERR_TOL
34
+ expect((described_class.matmul(mat_d, mat_c.transpose) - dot_mat_mat(mat_d, mat_c.transpose)).abs).to be < ERR_TOL
35
+ expect((described_class.matmul(mat_d, mat_z.transpose) - dot_mat_mat(mat_d, mat_z.transpose)).abs).to be < ERR_TOL
36
+ expect((described_class.matmul(mat_c, mat_c.transpose) - dot_mat_mat(mat_c, mat_c.transpose)).abs).to be < ERR_TOL
37
+ expect((described_class.matmul(mat_c, mat_z.transpose) - dot_mat_mat(mat_c, mat_z.transpose)).abs).to be < ERR_TOL
38
+ expect((described_class.matmul(mat_z, mat_z.transpose) - dot_mat_mat(mat_z, mat_z.transpose)).abs).to be < ERR_TOL
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'matrix_power' do
7
+ let(:m) { 5 }
8
+ let(:mat_a) { rand_square_real_mat(m) }
9
+
10
+ it 'raises ShapeError given a rectangular matrix' do
11
+ expect { described_class.matrix_power(Numo::DFloat.new(2, 4).rand, 2) }.to raise_error(Numo::NArray::ShapeError)
12
+ end
13
+
14
+ it 'raises ArgumentError given a non-integer exponent' do
15
+ expect { described_class.matrix_power(Numo::DFloat.new(2, 2).rand, 0.5) }.to raise_error(ArgumentError)
16
+ end
17
+
18
+ it 'calculates a square matrix to the n-th power' do
19
+ expect(described_class.matrix_power(mat_a, 0)).to eq(Numo::DFloat.eye(m))
20
+ pow_mat_a = described_class.matrix_power(mat_a, 2)
21
+ expect((pow_mat_a - mat_a.dot(mat_a)).abs.max).to be < ERR_TOL
22
+ pow_mat_a = described_class.matrix_power(mat_a, 5)
23
+ expect((pow_mat_a - (((mat_a.dot(mat_a)).dot(mat_a)).dot(mat_a)).dot(mat_a)).abs.max).to be < ERR_TOL
24
+ end
25
+
26
+ it 'calculates the inverse of a square matrix' do
27
+ inv_mat_a = described_class.matrix_power(mat_a, -1)
28
+ expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'matrix_rank' do
7
+ let(:r) { 2 }
8
+ let(:m) { 6 }
9
+ let(:n) { 3 }
10
+ let(:mat_a) { rand_rect_real_mat(m, r).dot(rand_rect_real_mat(r, n)) }
11
+ let(:mat_b) { rand_rect_complex_mat(m, r).dot(rand_rect_complex_mat(r, n)) }
12
+
13
+ it 'raises ArgumentError given a invalid driver option' do
14
+ expect { described_class.matrix_rank(mat_a, driver: 'foo') }.to raise_error(ArgumentError)
15
+ end
16
+
17
+ it 'calculate the matrix rank of a real matrix' do
18
+ expect(described_class.matrix_rank(mat_a)).to eq(r)
19
+ end
20
+
21
+ it 'calculate the matrix rank of a real matrix with divide and conquer method' do
22
+ expect(described_class.matrix_rank(mat_a, driver: 'sdd')).to eq(r)
23
+ end
24
+
25
+ it 'calculate the matrix rank of a complex matrix' do
26
+ expect(described_class.matrix_rank(mat_b)).to eq(r)
27
+ end
28
+
29
+ it 'calculate the matrix rank of a complex matrix with divide and conquer method' do
30
+ expect(described_class.matrix_rank(mat_b, driver: 'sdd')).to eq(r)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'norm' do
7
+ let(:m) { 6 }
8
+ let(:n) { 3 }
9
+ let(:mat_a) { rand_rect_real_mat(m, n) }
10
+ let(:mat_b) { rand_rect_complex_mat(m, n) }
11
+ let(:vec_a) { rand_real_vec(m) }
12
+ let(:vec_b) { rand_complex_vec(m) }
13
+ let(:vec_s) do
14
+ a = rand_real_vec(10)
15
+ a * (a > 0.0)
16
+ end
17
+ let(:vec_t) do
18
+ b = rand_real_vec(10) > 0.0
19
+ rand_complex_vec(10) * b
20
+ end
21
+
22
+ it 'raises ArgumentError given an invalid order option' do
23
+ expect { described_class.norm(mat_a, 0) }.to raise_error(ArgumentError)
24
+ expect { described_class.norm(mat_a, 5) }.to raise_error(ArgumentError)
25
+ expect { described_class.norm(mat_a, 'frobenius') }.to raise_error(ArgumentError)
26
+ expect { described_class.norm(vec_a, 'fro') }.to raise_error(ArgumentError)
27
+ end
28
+
29
+ it 'calculates the Froubenius norm of a real matrix' do
30
+ norm = Math.sqrt(mat_a.transpose.dot(mat_a).trace)
31
+ expect(described_class.norm(mat_a)).to be_within(norm).of(ERR_TOL)
32
+ expect(described_class.norm(mat_a, 'fro')).to be_within(norm).of(ERR_TOL)
33
+ end
34
+
35
+ it 'calculates the L1 norm of a real matrix' do
36
+ expect(described_class.norm(mat_a, 1)).to be_within(mat_a.abs.sum(axis: -2).max).of(ERR_TOL)
37
+ end
38
+
39
+ it 'calculates the L2 norm of a real matrix' do
40
+ expect(described_class.norm(mat_a, 2)).to be_within(described_class.svdvals(mat_a).max).of(ERR_TOL)
41
+ end
42
+
43
+ it 'calculates the inifinity norm of a real matrix' do
44
+ expect(described_class.norm(mat_a, 'inf')).to be_within(mat_a.abs.sum(axis: -1).max).of(ERR_TOL)
45
+ end
46
+
47
+ it 'calculates the Froubenius norm of a complex matrix' do
48
+ norm = Math.sqrt(mat_b.transpose.conj.dot(mat_b).trace.real)
49
+ expect(described_class.norm(mat_b)).to be_within(norm).of(ERR_TOL)
50
+ expect(described_class.norm(mat_b, 'fro')).to be_within(norm).of(ERR_TOL)
51
+ end
52
+
53
+ it 'calculates the L1 norm of a complex matrix' do
54
+ expect(described_class.norm(mat_b, 1)).to be_within(mat_b.abs.sum(axis: -2).max).of(ERR_TOL)
55
+ end
56
+
57
+ it 'calculates the L2 norm of a complex matrix' do
58
+ expect(described_class.norm(mat_b, 2)).to be_within(described_class.svdvals(mat_b).max).of(ERR_TOL)
59
+ end
60
+
61
+ it 'calculates the inifinity norm of a complex matrix' do
62
+ expect(described_class.norm(mat_b, 'inf')).to be_within(mat_b.abs.sum(axis: -1).max).of(ERR_TOL)
63
+ end
64
+
65
+ it 'calculates the L0 norm of a complex vector' do
66
+ expect(described_class.norm(vec_t, 0).real).to be_within((vec_t.abs.ne(0)).count).of(ERR_TOL)
67
+ end
68
+
69
+ it 'calculates the L1 norm of a complex vector' do
70
+ expect(described_class.norm(vec_b, 1)).to be_within(vec_b.abs.sum).of(ERR_TOL)
71
+ end
72
+
73
+ it 'calculates the L2 norm of a complex vector' do
74
+ expect(described_class.norm(vec_b, 2)).to be_within(Math.sqrt((vec_b.abs**2).sum)).of(ERR_TOL)
75
+ end
76
+
77
+ it 'calculates the infinity norm of a complex vector' do
78
+ expect(described_class.norm(vec_b, 'inf')).to be_within(vec_b.abs.max).of(ERR_TOL)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'pinv' do
7
+ let(:m) { 5 }
8
+ let(:n) { 3 }
9
+ let(:mat_a) { rand_rect_real_mat(m, n) }
10
+ let(:mat_b) { rand_rect_complex_mat(m, n) }
11
+
12
+ it 'raises ArgumentError given a invalid driver option' do
13
+ expect { described_class.pinv(mat_a, driver: 'foo') }.to raise_error(ArgumentError)
14
+ end
15
+
16
+ it 'calculates the (Moore-Penrose) pseudo-inverse of a rectangular real matrix using svd' do
17
+ inv_mat_a = described_class.pinv(mat_a, driver: 'svd')
18
+ expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
19
+ inv_mat_a = described_class.pinv(mat_a, driver: 'sdd')
20
+ expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
21
+ end
22
+
23
+ it 'calculates the (Moore-Penrose) pseudo-inverse of a rectangular complex matrix using svd' do
24
+ inv_mat_b = described_class.pinv(mat_b, driver: 'svd')
25
+ expect((inv_mat_b.dot(mat_b) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
26
+ inv_mat_b = described_class.pinv(mat_b, driver: 'sdd')
27
+ expect((inv_mat_b.dot(mat_b) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
28
+ end
29
+
30
+ it 'calculates the (Moore-Penrose) pseudo-inverse of a rectangular real matrix using lstsq' do
31
+ inv_mat_a = described_class.pinv(mat_a, driver: 'lsd')
32
+ expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
33
+ inv_mat_a = described_class.pinv(mat_a, driver: 'lss')
34
+ expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
35
+ inv_mat_a = described_class.pinv(mat_a, driver: 'lsy')
36
+ expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
37
+ end
38
+
39
+ it 'calculates the (Moore-Penrose) pseudo-inverse of a rectangular complex matrix using lstsq' do
40
+ inv_mat_b = described_class.pinv(mat_b, driver: 'lsd')
41
+ expect((inv_mat_b.dot(mat_b) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
42
+ inv_mat_b = described_class.pinv(mat_b, driver: 'lss')
43
+ expect((inv_mat_b.dot(mat_b) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
44
+ inv_mat_b = described_class.pinv(mat_b, driver: 'lsy')
45
+ expect((inv_mat_b.dot(mat_b) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'qr' do
7
+ let(:m) { 5 }
8
+ let(:n) { 3 }
9
+ let(:mat_a) { rand_rect_real_mat(m, n) }
10
+ let(:mat_b) { rand_rect_complex_mat(m, n) }
11
+
12
+ it 'raises ArgumentError given a invalid mode option' do
13
+ expect { described_class.qr(Numo::DFloat.new(2, 4).rand, mode: 'foo') }.to raise_error(ArgumentError)
14
+ end
15
+
16
+ it 'calculates the QR factorization of a real matrix with reduce mode' do
17
+ q, r = described_class.qr(mat_a, mode: 'reduce')
18
+ expect((q.dot(r) - mat_a).abs.max).to be < ERR_TOL
19
+ expect((q.transpose.dot(q) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
20
+ q, r = described_class.qr(mat_a.transpose, mode: 'reduce')
21
+ expect((q.dot(r) - mat_a.transpose).abs.max).to be < ERR_TOL
22
+ expect((q.transpose.dot(q) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
23
+ end
24
+
25
+ it 'calculates the QR factorization of a complex matrix with reduce mode' do
26
+ q, r = described_class.qr(mat_b, mode: 'reduce')
27
+ expect((q.dot(r) - mat_b).abs.max).to be < ERR_TOL
28
+ expect((q.transpose.conj.dot(q) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
29
+ q, r = described_class.qr(mat_b.transpose.conj, mode: 'reduce')
30
+ expect((q.dot(r) - mat_b.transpose.conj).abs.max).to be < ERR_TOL
31
+ expect((q.transpose.conj.dot(q) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
32
+ end
33
+
34
+ it 'calculates the QR factorization of a real matrix with r mode' do
35
+ q_, r = described_class.qr(mat_a, mode: 'reduce')
36
+ r2 = described_class.qr(mat_a, mode: 'r')
37
+ expect((r - r2).abs.max).to be < ERR_TOL
38
+ q_, r = described_class.qr(mat_a.transpose, mode: 'reduce')
39
+ r2 = described_class.qr(mat_a.transpose, mode: 'r')
40
+ expect((r - r2).abs.max).to be < ERR_TOL
41
+ end
42
+
43
+ it 'calculates the QR factorization of a complex matrix with r mode' do
44
+ q_, r = described_class.qr(mat_b, mode: 'reduce')
45
+ r2 = described_class.qr(mat_b, mode: 'r')
46
+ expect((r - r2).abs.max).to be < ERR_TOL
47
+ q_, r = described_class.qr(mat_b.transpose.conj, mode: 'reduce')
48
+ r2 = described_class.qr(mat_b.transpose.conj, mode: 'r')
49
+ expect((r - r2).abs.max).to be < ERR_TOL
50
+ end
51
+
52
+ it 'calculates the QR factorization of a real matrix with economic mode' do
53
+ q, r = described_class.qr(mat_a, mode: 'economic')
54
+ expect(q.shape[0]).to eq(m)
55
+ expect(q.shape[1]).to eq(n)
56
+ expect(r.shape[0]).to eq(n)
57
+ expect(r.shape[1]).to eq(n)
58
+ expect((q.dot(r) - mat_a).abs.max).to be < ERR_TOL
59
+ end
60
+
61
+ it 'calculates the QR factorization of a complex matrix with economic mode' do
62
+ q, r = described_class.qr(mat_b, mode: 'economic')
63
+ expect(q.shape[0]).to eq(m)
64
+ expect(q.shape[1]).to eq(n)
65
+ expect(r.shape[0]).to eq(n)
66
+ expect(r.shape[1]).to eq(n)
67
+ expect((q.dot(r) - mat_b).abs.max).to be < ERR_TOL
68
+ end
69
+
70
+ it 'calculates the QR factorization of a real matrix with raw mode' do
71
+ q_, r = described_class.qr(mat_a)
72
+ q, tau = described_class.qr(mat_a, mode: 'raw')
73
+ expect((q.triu - r).abs.max).to be < ERR_TOL
74
+ end
75
+
76
+ it 'calculates the QR factorization of a complex matrix with raw mode' do
77
+ q_, r = described_class.qr(mat_b)
78
+ q, tau = described_class.qr(mat_b, mode: 'raw')
79
+ expect((q.triu - r).abs.max).to be < ERR_TOL
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'slogdet' 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 natural logarithm of the dererminant of a square real matrix' do
12
+ logdet = Math.log(described_class.eigvals(mat_a).prod.abs)
13
+ expect((described_class.slogdet(mat_a)[1] - logdet).abs).to be < ERR_TOL
14
+ end
15
+
16
+ it 'calculates natural logarithm of the dererminant of a square complex matrix' do
17
+ logdet = Math.log(described_class.eigvals(mat_b).prod.abs)
18
+ expect((described_class.slogdet(mat_b)[1] - logdet).abs).to be < ERR_TOL
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Numo::Linalg do
6
+ describe 'solve' do
7
+ let(:m) { 5 }
8
+ let(:n) { 3 }
9
+ let(:mat_a) { rand_square_real_mat(m) }
10
+ let(:mat_b) { rand_rect_real_mat(m, n) }
11
+ let(:vec_b) { rand_real_vec(m) }
12
+ let(:mat_c) { rand_square_complex_mat(m) }
13
+ let(:mat_d) { rand_rect_complex_mat(m, n) }
14
+ let(:vec_d) { rand_complex_vec(m) }
15
+ let(:mat_s) { rand_symmetric_mat(m) }
16
+ let(:mat_h) { rand_hermitian_mat(m) }
17
+ let(:mat_p) { mat_s.dot(mat_s.transpose) }
18
+ let(:mat_q) { mat_h.dot(mat_h.transpose.conj) }
19
+
20
+ it 'raises ArgumentError given a invalid driver option' do
21
+ expect { described_class.solve(mat_a, vec_b, driver: 'foo') }.to raise_error(ArgumentError)
22
+ end
23
+
24
+ it 'raises ShapeError when the number of rows of matrices are different' do
25
+ expect { described_class.solve(mat_a, mat_b.transpose) }.to raise_error(Numo::NArray::ShapeError)
26
+ end
27
+
28
+ it 'raises ShapeError given a rectangular matrix as matrix A' do
29
+ expect { described_class.solve(mat_b, mat_a) }.to raise_error(Numo::NArray::ShapeError)
30
+ end
31
+
32
+ it 'solves the linear equation A x = b with a square real matrix A' do
33
+ vec_x = described_class.solve(mat_a, vec_b)
34
+ expect((mat_a.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
35
+ mat_x = described_class.solve(mat_a, mat_b)
36
+ expect((mat_a.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
37
+ vec_x = described_class.solve(mat_a, vec_d)
38
+ expect((mat_a.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
39
+ mat_x = described_class.solve(mat_a, mat_d)
40
+ expect((mat_a.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
41
+ end
42
+
43
+ it 'solves the linear equation A x = b with a square complex matrix A' do
44
+ vec_x = described_class.solve(mat_c, vec_b)
45
+ expect((mat_c.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
46
+ mat_x = described_class.solve(mat_c, mat_b)
47
+ expect((mat_c.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
48
+ vec_x = described_class.solve(mat_c, vec_d)
49
+ expect((mat_c.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
50
+ mat_x = described_class.solve(mat_c, mat_d)
51
+ expect((mat_c.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
52
+ end
53
+
54
+ it 'solves the linear equation A x = b with a symmetric matrix A' do
55
+ vec_x = described_class.solve(mat_s, vec_b, driver: 'sym')
56
+ expect((mat_s.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
57
+ mat_x = described_class.solve(mat_s, mat_b, driver: 'sym')
58
+ expect((mat_s.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
59
+ vec_x = described_class.solve(mat_s, vec_d, driver: 'sym')
60
+ expect((mat_s.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
61
+ mat_x = described_class.solve(mat_s, mat_d, driver: 'sym')
62
+ expect((mat_s.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
63
+ end
64
+
65
+ it 'solves the linear equation A x = b with a hermitian matrix A' do
66
+ vec_x = described_class.solve(mat_h, vec_b, driver: 'her')
67
+ expect((mat_h.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
68
+ mat_x = described_class.solve(mat_h, mat_b, driver: 'her')
69
+ expect((mat_h.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
70
+ vec_x = described_class.solve(mat_h, vec_d, driver: 'her')
71
+ expect((mat_h.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
72
+ mat_x = described_class.solve(mat_h, mat_d, driver: 'her')
73
+ expect((mat_h.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
74
+ end
75
+
76
+ it 'solves the linear equation A x = b with a symmetric positive-definite matrix A' do
77
+ vec_x = described_class.solve(mat_p, vec_b, driver: 'pos')
78
+ expect((mat_p.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
79
+ mat_x = described_class.solve(mat_p, mat_b, driver: 'pos')
80
+ expect((mat_p.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
81
+ vec_x = described_class.solve(mat_p, vec_d, driver: 'pos')
82
+ expect((mat_p.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
83
+ mat_x = described_class.solve(mat_p, mat_d, driver: 'pos')
84
+ expect((mat_p.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
85
+ end
86
+
87
+ it 'solves the linear equation A x = b with a hermitian positive-definite matrix A' do
88
+ vec_x = described_class.solve(mat_q, vec_b, driver: 'pos')
89
+ expect((mat_q.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
90
+ mat_x = described_class.solve(mat_q, mat_b, driver: 'pos')
91
+ expect((mat_q.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
92
+ vec_x = described_class.solve(mat_q, vec_d, driver: 'pos')
93
+ expect((mat_q.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
94
+ mat_x = described_class.solve(mat_q, mat_d, driver: 'pos')
95
+ expect((mat_q.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
96
+ end
97
+ end
98
+ end