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.
- checksums.yaml +4 -4
- data/README.md +7 -3
- data/ext/numo/linalg/blas/extconf.rb +1 -2
- data/ext/numo/linalg/blas/numo_blas.h +6 -0
- data/ext/numo/linalg/blas/tmpl/mv.c +3 -2
- data/ext/numo/linalg/lapack/gen/spec.rb +5 -0
- data/ext/numo/linalg/lapack/lapack.c +29 -0
- data/ext/numo/linalg/lapack/numo_lapack.h +3 -0
- data/ext/numo/linalg/lapack/tmpl/gqr.c +1 -1
- data/ext/numo/linalg/lapack/tmpl/sygvx.c +130 -0
- data/ext/numo/linalg/mkmf_linalg.rb +2 -19
- data/lib/numo/linalg/function.rb +168 -77
- data/lib/numo/linalg/loader.rb +6 -14
- data/lib/numo/linalg/version.rb +1 -1
- data/numo-linalg.gemspec +2 -1
- data/spec/linalg/autoloader_spec.rb +27 -0
- data/spec/linalg/function/cho_fact_spec.rb +31 -0
- data/spec/linalg/function/cho_inv_spec.rb +39 -0
- data/spec/linalg/function/cho_solve_spec.rb +66 -0
- data/spec/linalg/function/cholesky_spec.rb +43 -0
- data/spec/linalg/function/cond_spec.rb +57 -0
- data/spec/linalg/function/det_spec.rb +21 -0
- data/spec/linalg/function/dot_spec.rb +84 -0
- data/spec/linalg/function/eig_spec.rb +53 -0
- data/spec/linalg/function/eigh_spec.rb +81 -0
- data/spec/linalg/function/eigvals_spec.rb +27 -0
- data/spec/linalg/function/eigvalsh_spec.rb +60 -0
- data/spec/linalg/function/inv_spec.rb +57 -0
- data/spec/linalg/function/ldl_spec.rb +51 -0
- data/spec/linalg/function/lstsq_spec.rb +80 -0
- data/spec/linalg/function/lu_fact_spec.rb +34 -0
- data/spec/linalg/function/lu_inv_spec.rb +21 -0
- data/spec/linalg/function/lu_solve_spec.rb +40 -0
- data/spec/linalg/function/lu_spec.rb +46 -0
- data/spec/linalg/function/matmul_spec.rb +41 -0
- data/spec/linalg/function/matrix_power_spec.rb +31 -0
- data/spec/linalg/function/matrix_rank_spec.rb +33 -0
- data/spec/linalg/function/norm_spec.rb +81 -0
- data/spec/linalg/function/pinv_spec.rb +48 -0
- data/spec/linalg/function/qr_spec.rb +82 -0
- data/spec/linalg/function/slogdet_spec.rb +21 -0
- data/spec/linalg/function/solve_spec.rb +98 -0
- data/spec/linalg/function/svd_spec.rb +88 -0
- data/spec/linalg/function/svdvals_spec.rb +40 -0
- data/spec/spec_helper.rb +55 -0
- metadata +79 -6
- data/spec/lapack_spec.rb +0 -13
data/lib/numo/linalg/loader.rb
CHANGED
@@ -16,24 +16,16 @@ module Numo
|
|
16
16
|
if dir && !dir.empty?
|
17
17
|
base = File.join(dir,base)
|
18
18
|
end
|
19
|
-
|
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
|
-
|
29
|
-
|
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
|
data/lib/numo/linalg/version.rb
CHANGED
data/numo-linalg.gemspec
CHANGED
@@ -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
|