numo-linalg 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|