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
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'eigh' do
|
7
|
+
let(:m) { 5 }
|
8
|
+
let(:n) { 3 }
|
9
|
+
let(:mat_a) { rand_symmetric_mat(m) }
|
10
|
+
let(:mat_b) { rand_symmetric_mat(m) + 5.1 * Numo::DFloat.eye(m) } # for avoiding non-singular matrix
|
11
|
+
let(:mat_c) { rand_hermitian_mat(m) }
|
12
|
+
let(:mat_d) { rand_hermitian_mat(m) + 5.1 * Numo::DFloat.eye(m) } # for avoiding non-singular matrix
|
13
|
+
|
14
|
+
it 'raises ShapeError given a rectangular matrix' do
|
15
|
+
expect { described_class.eigh(Numo::DFloat.new(2, 4).rand) }.to raise_error(Numo::NArray::ShapeError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'finds eigenvalues and eigenvectors by solving eigenvalue problem for a symmetric matrix' do
|
19
|
+
v, w = described_class.eigh(mat_a)
|
20
|
+
r = w.transpose.dot(mat_a.dot(w))
|
21
|
+
r = r[r.diag_indices]
|
22
|
+
expect((v - r).abs.mean).to be < ERR_TOL
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'finds eigenvalues and eigenvectors by solving eigenvalue problem for a hermitian matrix' do
|
26
|
+
v, w = described_class.eigh(mat_c)
|
27
|
+
r = w.transpose.conj.dot(mat_c.dot(w))
|
28
|
+
r = r[r.diag_indices].real
|
29
|
+
expect((v - r).abs.mean).to be < ERR_TOL
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'finds eigenvalues and eigenvectors by solving generalized eigenvalue problem for symmetric matrices' do
|
33
|
+
v, w = described_class.eigh(mat_a, mat_b)
|
34
|
+
r = w.transpose.dot(mat_a.dot(w))
|
35
|
+
r = r[r.diag_indices]
|
36
|
+
e = w.transpose.dot(mat_b.dot(w))
|
37
|
+
e = e[e.diag_indices]
|
38
|
+
expect((v - r).abs.mean).to be < ERR_TOL
|
39
|
+
expect((Numo::DFloat.ones(m) - e).abs.mean).to be < ERR_TOL
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'finds eigenvalues and eigenvectors by solving generalized eigenvalue problem for hermitian matrices' do
|
43
|
+
v, w = described_class.eigh(mat_c, mat_d)
|
44
|
+
r = w.transpose.conj.dot(mat_c.dot(w))
|
45
|
+
r = r[r.diag_indices].real
|
46
|
+
e = w.transpose.conj.dot(mat_d.dot(w))
|
47
|
+
e = e[e.diag_indices].real
|
48
|
+
expect((v - r).abs.mean).to be < ERR_TOL
|
49
|
+
expect((Numo::DFloat.ones(m) - e).abs.mean).to be < ERR_TOL
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'finds eigenvalues and eigenvectors by solving generalized eigenvalue problem for symmetric and hermitian matrices' do
|
53
|
+
v, w = described_class.eigh(mat_a, mat_d)
|
54
|
+
r = w.transpose.conj.dot(mat_a.dot(w))
|
55
|
+
r = r[r.diag_indices].real
|
56
|
+
e = w.transpose.conj.dot(mat_d.dot(w))
|
57
|
+
e = e[e.diag_indices].real
|
58
|
+
expect((v - r).abs.mean).to be < ERR_TOL
|
59
|
+
expect((Numo::DFloat.ones(m) - e).abs.mean).to be < ERR_TOL
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'finds eigenvalues and eigenvectors for a symmetric matrix with divide and conquer algorithm' do
|
63
|
+
v, w = described_class.eigh(mat_a, turbo: true)
|
64
|
+
r = w.transpose.dot(mat_a.dot(w))
|
65
|
+
r = r[r.diag_indices]
|
66
|
+
expect((v - r).abs.mean).to be < ERR_TOL
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'finds eigenvalues and eigenvectors in the range of the specified indices' do
|
70
|
+
v1, w1 = described_class.eigh(mat_a, mat_b)
|
71
|
+
v2, w2 = described_class.eigh(mat_a, mat_b, vals_range: 0...n)
|
72
|
+
r = w2.transpose.dot(mat_a.dot(w2))
|
73
|
+
r = r[r.diag_indices]
|
74
|
+
e = w2.transpose.dot(mat_b.dot(w2))
|
75
|
+
e = e[e.diag_indices]
|
76
|
+
expect((v2 - r).abs.mean).to be < ERR_TOL
|
77
|
+
expect((v1[0...n] - r).abs.mean).to be < ERR_TOL
|
78
|
+
expect((Numo::DFloat.ones(n) - e).abs.mean).to be < ERR_TOL
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'eigvals' do
|
7
|
+
let(:m) { 5 }
|
8
|
+
let(:mat_a) { Numo::DFloat.new(m, m).rand - 0.5 }
|
9
|
+
let(:mat_b) { mat_a + Complex::I * (Numo::DFloat.new(m, m).rand - 0.5) }
|
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 for a square nonsymmetric matrix' do
|
16
|
+
w1 = described_class.eigvals(mat_a)
|
17
|
+
w2, = described_class.eig(mat_a, left: false, right: false)
|
18
|
+
expect((w1 - w2).abs.max).to be < ERR_TOL
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'finds eigenvalues for a square complex nonsymmetric matrix' do
|
22
|
+
w1 = described_class.eigvals(mat_b)
|
23
|
+
w2, = described_class.eig(mat_b, left: false, right: false)
|
24
|
+
expect((w1 - w2).abs.max).to be < ERR_TOL
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'eigvash' do
|
7
|
+
let(:m) { 5 }
|
8
|
+
let(:n) { 3 }
|
9
|
+
let(:mat_a) { rand_symmetric_mat(m) }
|
10
|
+
let(:mat_b) { rand_symmetric_mat(m) + 5.1 * Numo::DFloat.eye(m) } # for avoiding non-singular matrix
|
11
|
+
let(:mat_c) { rand_hermitian_mat(m) }
|
12
|
+
let(:mat_d) { rand_hermitian_mat(m) + 5.1 * Numo::DFloat.eye(m) } # for avoiding non-singular matrix
|
13
|
+
|
14
|
+
it 'raises ShapeError given a rectangular matrix' do
|
15
|
+
expect { described_class.eigh(Numo::DFloat.new(2, 4).rand) }.to raise_error(Numo::NArray::ShapeError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'finds eigenvalues by solving eigenvalue problem for a symmetric matrix' do
|
19
|
+
v1 = described_class.eigvalsh(mat_a)
|
20
|
+
v2, = described_class.eigh(mat_a)
|
21
|
+
expect((v1 - v2).abs.mean).to be < ERR_TOL
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'finds eigenvalues by solving eigenvalue problem for a hermitian matrix' do
|
25
|
+
v1 = described_class.eigvalsh(mat_c)
|
26
|
+
v2, = described_class.eigh(mat_c)
|
27
|
+
expect((v1 - v2).abs.mean).to be < ERR_TOL
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'finds eigenvalues by solving generalized eigenvalue problem for symmetric matrices' do
|
31
|
+
v1 = described_class.eigvalsh(mat_a, mat_b)
|
32
|
+
v2, = described_class.eigh(mat_a, mat_b)
|
33
|
+
expect((v1 - v2).abs.mean).to be < ERR_TOL
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'finds eigenvalues by solving generalized eigenvalue problem for hermitian matrices' do
|
37
|
+
v1 = described_class.eigvalsh(mat_c, mat_d)
|
38
|
+
v2, = described_class.eigh(mat_c, mat_d)
|
39
|
+
expect((v1 - v2).abs.mean).to be < ERR_TOL
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'finds eigenvalues by solving generalized eigenvalue problem for symmetric and hermitian matrices' do
|
43
|
+
v1 = described_class.eigvalsh(mat_a, mat_d)
|
44
|
+
v2, = described_class.eigh(mat_a, mat_d)
|
45
|
+
expect((v1 - v2).abs.mean).to be < ERR_TOL
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'finds eigenvalues for a symmetric matrix with divide and conquer algorithm' do
|
49
|
+
v1 = described_class.eigvalsh(mat_a, turbo: true)
|
50
|
+
v2, = described_class.eigh(mat_a, turbo: true)
|
51
|
+
expect((v1 - v2).abs.mean).to be < ERR_TOL
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'finds eigenvalues in the range of the specified indices' do
|
55
|
+
v1 = described_class.eigvalsh(mat_a, mat_b, vals_range: 0...n)
|
56
|
+
v2, = described_class.eigh(mat_a, mat_b, vals_range: 0...n)
|
57
|
+
expect((v1 - v2).abs.mean).to be < ERR_TOL
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'inv' do
|
7
|
+
let(:m) { 5 }
|
8
|
+
let(:mat_a) { rand_square_real_mat(m) }
|
9
|
+
let(:mat_b) { rand_square_complex_mat(m) }
|
10
|
+
let(:mat_s) { rand_symmetric_mat(m) }
|
11
|
+
let(:mat_h) { rand_hermitian_mat(m) }
|
12
|
+
|
13
|
+
it 'raises ArgumentError given a invalid driver option' do
|
14
|
+
expect { described_class.inv(mat_a, driver: 'foo') }.to raise_error(ArgumentError)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'calculates the inverse of a square real matrix solving linear equation' do
|
18
|
+
inv_mat_a = described_class.inv(mat_a, driver: 'gesv')
|
19
|
+
expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'calculates the inverse of a square complex matrix solving linear equation' do
|
23
|
+
inv_mat_b = described_class.inv(mat_b, driver: 'gesv')
|
24
|
+
expect((inv_mat_b.dot(mat_b) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'calculates the inverse of a symmetric matrix solving linear equation' do
|
28
|
+
inv_mat_s = described_class.inv(mat_s, driver: 'sysv')
|
29
|
+
expect((inv_mat_s.dot(mat_s) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'calculates the inverse of a hermitian matrix solving linear equation' do
|
33
|
+
inv_mat_h = described_class.inv(mat_h, driver: 'hesv')
|
34
|
+
expect((inv_mat_h.dot(mat_h) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'calculates the inverse of a square real matrix using the LU factorization' do
|
38
|
+
inv_mat_a = described_class.inv(mat_a, driver: 'getrf')
|
39
|
+
expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'calculates the inverse of a square complex matrix using the LU factorization' do
|
43
|
+
inv_mat_b = described_class.inv(mat_b, driver: 'getrf')
|
44
|
+
expect((inv_mat_b.dot(mat_b) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'calculates the inverse of a symmetric matrix using the LU factorization' do
|
48
|
+
inv_mat_s = described_class.inv(mat_s, driver: 'sytrf')
|
49
|
+
expect((inv_mat_s.dot(mat_s) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'calculates the inverse of a hermitian matrix using the LU factorization' do
|
53
|
+
inv_mat_h = described_class.inv(mat_h, driver: 'hetrf')
|
54
|
+
expect((inv_mat_h.dot(mat_h) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'ldl' do
|
7
|
+
let(:m) { 5 }
|
8
|
+
let(:mat_s) { rand_symmetric_mat(m) }
|
9
|
+
let(:mat_c) { rand_symmetric_mat(m) + Complex::I * rand_symmetric_mat(m) }
|
10
|
+
let(:mat_h) { rand_hermitian_mat(m) }
|
11
|
+
|
12
|
+
it 'raises ShapeError given a vector' do
|
13
|
+
expect { described_class.ldl(Numo::DFloat.new(3).rand) }.to raise_error(Numo::NArray::ShapeError)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'raises ShapeError given a rectangular matrix' do
|
17
|
+
expect { described_class.ldl(Numo::DFloat.new(2, 4).rand) }.to raise_error(Numo::NArray::ShapeError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'raises ArgumentError given an invalid uplo option' do
|
21
|
+
expect { described_class.ldl(mat_s, uplo: 'A') }.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'calculates the LDLt or Bunch-Kaufman factorization of a symmetric real matrix' do
|
25
|
+
mat_u, mat_d, perm = described_class.ldl(mat_s, uplo: 'U')
|
26
|
+
expect((mat_s - mat_u.dot(mat_d.dot(mat_u.transpose))).abs.max).to be < ERR_TOL
|
27
|
+
expect(mat_u[perm, true].tril(-1)).to eq(Numo::DFloat.zeros(m, m))
|
28
|
+
mat_l, mat_d, perm = described_class.ldl(mat_s, uplo: 'L')
|
29
|
+
expect((mat_s - mat_l.dot(mat_d.dot(mat_l.transpose))).abs.max).to be < ERR_TOL
|
30
|
+
expect(mat_l[perm, true].triu(1)).to eq(Numo::DFloat.zeros(m, m))
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'calculates the LDLt or Bunch-Kaufman factorization of a symmetric complex matrix' do
|
34
|
+
mat_u, mat_d, perm = described_class.ldl(mat_c, uplo: 'U', hermitian: false)
|
35
|
+
expect((mat_c - mat_u.dot(mat_d.dot(mat_u.transpose))).abs.max).to be < ERR_TOL
|
36
|
+
expect(mat_u[perm, true].tril(-1)).to eq(Numo::DComplex.zeros(m, m))
|
37
|
+
mat_l, mat_d, perm = described_class.ldl(mat_c, uplo: 'L', hermitian: false)
|
38
|
+
expect((mat_c - mat_l.dot(mat_d.dot(mat_l.transpose))).abs.max).to be < ERR_TOL
|
39
|
+
expect(mat_l[perm, true].triu(1)).to eq(Numo::DComplex.zeros(m, m))
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'calculates the LDLt or Bunch-Kaufman factorization of a hermitian matrix' do
|
43
|
+
mat_u, mat_d, perm = described_class.ldl(mat_h, uplo: 'U')
|
44
|
+
expect((mat_h - mat_u.dot(mat_d.dot(mat_u.transpose.conj))).abs.max).to be < ERR_TOL
|
45
|
+
expect(mat_u[perm, true].tril(-1)).to eq(Numo::DComplex.zeros(m, m))
|
46
|
+
mat_l, mat_d, perm = described_class.ldl(mat_h, uplo: 'L')
|
47
|
+
expect((mat_h - mat_l.dot(mat_d.dot(mat_l.transpose.conj))).abs.max).to be < ERR_TOL
|
48
|
+
expect(mat_l[perm, true].triu(1)).to eq(Numo::DComplex.zeros(m, m))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'lstsq' do
|
7
|
+
#let(:m) { 5 }
|
8
|
+
#let(:n) { 3 }
|
9
|
+
let(:m) { 3 }
|
10
|
+
let(:n) { 8 }
|
11
|
+
let(:nrhs) { 2 }
|
12
|
+
let(:mat_a) { rand_rect_real_mat(m, n) }
|
13
|
+
let(:mat_b) { rand_rect_real_mat(m, nrhs) }
|
14
|
+
let(:vec_b) { rand_real_vec(m) }
|
15
|
+
let(:mat_c) { rand_rect_complex_mat(m, n) }
|
16
|
+
let(:mat_d) { rand_rect_complex_mat(m, nrhs) }
|
17
|
+
let(:vec_d) { rand_complex_vec(m) }
|
18
|
+
|
19
|
+
it 'raises ArgumentError given a invalid driver option' do
|
20
|
+
expect { described_class.lstsq(mat_a, vec_b, driver: 'foo') }.to raise_error(ArgumentError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'solves a linear least square problem min||b-Ax|| using the singuar value decomposition' do
|
24
|
+
vec_x, = described_class.lstsq(mat_a, vec_b, driver: 'lss')
|
25
|
+
expect(((vec_b - mat_a.dot(vec_x))**2).sum).to be < ERR_TOL
|
26
|
+
mat_x, = described_class.lstsq(mat_a, mat_b, driver: 'lss')
|
27
|
+
expect(((mat_b - mat_a.dot(mat_x))**2).sum(0).max).to be < ERR_TOL
|
28
|
+
vec_x, = described_class.lstsq(mat_a, vec_d, driver: 'lss')
|
29
|
+
expect(((vec_d - mat_a.dot(vec_x))**2).abs.sum).to be < ERR_TOL
|
30
|
+
mat_x, = described_class.lstsq(mat_a, mat_d, driver: 'lss')
|
31
|
+
expect(((mat_d - mat_a.dot(mat_x))**2).abs.sum(0).max).to be < ERR_TOL
|
32
|
+
vec_x, = described_class.lstsq(mat_c, vec_d, driver: 'lss')
|
33
|
+
expect(((vec_d - mat_c.dot(vec_x))**2).abs.sum).to be < ERR_TOL
|
34
|
+
mat_x, = described_class.lstsq(mat_c, mat_d, driver: 'lss')
|
35
|
+
expect(((mat_d - mat_c.dot(mat_x))**2).abs.sum(0).max).to be < ERR_TOL
|
36
|
+
vec_x, = described_class.lstsq(mat_c, vec_b, driver: 'lss')
|
37
|
+
expect(((vec_b - mat_c.dot(vec_x))**2).abs.sum).to be < ERR_TOL
|
38
|
+
mat_x, = described_class.lstsq(mat_c, mat_b, driver: 'lss')
|
39
|
+
expect(((mat_b - mat_c.dot(mat_x))**2).abs.sum(0).max).to be < ERR_TOL
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'solves a linear least square problem min||b-Ax|| using the singuar value decomposition with divide and conquer method' do
|
43
|
+
vec_x, = described_class.lstsq(mat_a, vec_b, driver: 'lsd')
|
44
|
+
expect(((vec_b - mat_a.dot(vec_x))**2).sum).to be < ERR_TOL
|
45
|
+
mat_x, = described_class.lstsq(mat_a, mat_b, driver: 'lsd')
|
46
|
+
expect(((mat_b - mat_a.dot(mat_x))**2).sum(0).max).to be < ERR_TOL
|
47
|
+
vec_x, = described_class.lstsq(mat_a, vec_d, driver: 'lsd')
|
48
|
+
expect(((vec_d - mat_a.dot(vec_x))**2).abs.sum).to be < ERR_TOL
|
49
|
+
mat_x, = described_class.lstsq(mat_a, mat_d, driver: 'lsd')
|
50
|
+
expect(((mat_d - mat_a.dot(mat_x))**2).abs.sum(0).max).to be < ERR_TOL
|
51
|
+
vec_x, = described_class.lstsq(mat_c, vec_d, driver: 'lsd')
|
52
|
+
expect(((vec_d - mat_c.dot(vec_x))**2).abs.sum).to be < ERR_TOL
|
53
|
+
mat_x, = described_class.lstsq(mat_c, mat_d, driver: 'lsd')
|
54
|
+
expect(((mat_d - mat_c.dot(mat_x))**2).abs.sum(0).max).to be < ERR_TOL
|
55
|
+
vec_x, = described_class.lstsq(mat_c, vec_b, driver: 'lsd')
|
56
|
+
expect(((vec_b - mat_c.dot(vec_x))**2).abs.sum).to be < ERR_TOL
|
57
|
+
mat_x, = described_class.lstsq(mat_c, mat_b, driver: 'lsd')
|
58
|
+
expect(((mat_b - mat_c.dot(mat_x))**2).abs.sum(0).max).to be < ERR_TOL
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'solves a linear least square problem min||b-Ax|| using a complete orthogonal factorization of A' do
|
62
|
+
vec_x, = described_class.lstsq(mat_a, vec_b, driver: 'lsy')
|
63
|
+
expect(((vec_b - mat_a.dot(vec_x))**2).sum).to be < ERR_TOL
|
64
|
+
mat_x, = described_class.lstsq(mat_a, mat_b, driver: 'lsy')
|
65
|
+
expect(((mat_b - mat_a.dot(mat_x))**2).sum(0).max).to be < ERR_TOL
|
66
|
+
vec_x, = described_class.lstsq(mat_a, vec_d, driver: 'lsy')
|
67
|
+
expect(((vec_d - mat_a.dot(vec_x))**2).abs.sum).to be < ERR_TOL
|
68
|
+
mat_x, = described_class.lstsq(mat_a, mat_d, driver: 'lsy')
|
69
|
+
expect(((mat_d - mat_a.dot(mat_x))**2).abs.sum(0).max).to be < ERR_TOL
|
70
|
+
vec_x, = described_class.lstsq(mat_c, vec_d, driver: 'lsy')
|
71
|
+
expect(((vec_d - mat_c.dot(vec_x))**2).abs.sum).to be < ERR_TOL
|
72
|
+
mat_x, = described_class.lstsq(mat_c, mat_d, driver: 'lsy')
|
73
|
+
expect(((mat_d - mat_c.dot(mat_x))**2).abs.sum(0).max).to be < ERR_TOL
|
74
|
+
vec_x, = described_class.lstsq(mat_c, vec_b, driver: 'lsy')
|
75
|
+
expect(((vec_b - mat_c.dot(vec_x))**2).abs.sum).to be < ERR_TOL
|
76
|
+
mat_x, = described_class.lstsq(mat_c, mat_b, driver: 'lsy')
|
77
|
+
expect(((mat_b - mat_c.dot(mat_x))**2).abs.sum(0).max).to be < ERR_TOL
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'lu_fact' 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
|
+
def permutation_mat(ipiv)
|
13
|
+
Numo::DFloat.eye(m).tap do |mat|
|
14
|
+
ipiv.to_a.each_with_index { |a, b| mat[[a - 1, b], true] = mat[[b, a - 1], true].dup }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'calculates the LU factorization of a rectangular real matrix' do
|
19
|
+
lu, ipiv = described_class.lu_fact(mat_a)
|
20
|
+
mat_l = lu.tril.tap { |mat| mat[mat.diag_indices(0)] = 1.0 }
|
21
|
+
mat_u = lu.triu[0...n, 0...n]
|
22
|
+
mat_p = permutation_mat(ipiv)
|
23
|
+
expect((mat_p.dot(mat_a) - mat_l.dot(mat_u)).abs.max).to be < ERR_TOL
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'calculates the LU factorization of a rectangular complex matrix' do
|
27
|
+
lu, ipiv = described_class.lu_fact(mat_b)
|
28
|
+
mat_l = lu.tril.tap { |mat| mat[mat.diag_indices(0)] = 1.0 }
|
29
|
+
mat_u = lu.triu[0...n, 0...n]
|
30
|
+
mat_p = permutation_mat(ipiv)
|
31
|
+
expect((mat_p.dot(mat_b) - mat_l.dot(mat_u)).abs.max).to be < ERR_TOL
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'lu_inv' 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 inverse of a square real matrix' do
|
12
|
+
inv_mat_a = described_class.inv(mat_a)
|
13
|
+
expect((inv_mat_a.dot(mat_a) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'calculates the inverse of a square complex matrix' do
|
17
|
+
inv_mat_b = described_class.inv(mat_b)
|
18
|
+
expect((inv_mat_b.dot(mat_b) - Numo::DFloat.eye(m)).abs.max).to be < ERR_TOL
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'lu_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
|
+
|
16
|
+
it 'solves the linear equation A x = b with a square real matrix A' do
|
17
|
+
lu, ipiv = described_class.lu_fact(mat_a)
|
18
|
+
vec_x = described_class.lu_solve(lu, ipiv, vec_b)
|
19
|
+
expect((mat_a.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
|
20
|
+
mat_x = described_class.lu_solve(lu, ipiv, mat_b)
|
21
|
+
expect((mat_a.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
|
22
|
+
vec_x = described_class.lu_solve(lu, ipiv, vec_d)
|
23
|
+
expect((mat_a.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
|
24
|
+
mat_x = described_class.lu_solve(lu, ipiv, mat_d)
|
25
|
+
expect((mat_a.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'solves the linear equation A x = b with a square complex matrix A' do
|
29
|
+
lu, ipiv = described_class.lu_fact(mat_c)
|
30
|
+
vec_x = described_class.lu_solve(lu, ipiv, vec_b)
|
31
|
+
expect((mat_c.dot(vec_x) - vec_b).abs.max).to be < ERR_TOL
|
32
|
+
mat_x = described_class.lu_solve(lu, ipiv, mat_b)
|
33
|
+
expect((mat_c.dot(mat_x) - mat_b).abs.max).to be < ERR_TOL
|
34
|
+
vec_x = described_class.lu_solve(lu, ipiv, vec_d)
|
35
|
+
expect((mat_c.dot(vec_x) - vec_d).abs.max).to be < ERR_TOL
|
36
|
+
mat_x = described_class.lu_solve(lu, ipiv, mat_d)
|
37
|
+
expect((mat_c.dot(mat_x) - mat_d).abs.max).to be < ERR_TOL
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|