nulin 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/doc/BSDL +22 -0
- data/doc/COPYING +10 -0
- data/doc/README.en +31 -0
- data/doc/README.ja +36 -0
- data/ext/extconf.rb +33 -0
- data/ext/nulin_native.c +637 -0
- data/lib/narray_extext.rb +113 -0
- data/lib/nulin.rb +51 -0
- data/lib/nulin/cholesky.rb +62 -0
- data/lib/nulin/det.rb +17 -0
- data/lib/nulin/eigensystem.rb +191 -0
- data/lib/nulin/gemm.rb +53 -0
- data/lib/nulin/lls.rb +133 -0
- data/lib/nulin/qr.rb +68 -0
- data/lib/nulin/svd.rb +84 -0
- data/tests/run_test.rb +17 -0
- data/tests/test_cholesky.rb +39 -0
- data/tests/test_det.rb +11 -0
- data/tests/test_eigensystem.rb +71 -0
- data/tests/test_gemm.rb +57 -0
- data/tests/test_lls.rb +51 -0
- data/tests/test_narray.rb +100 -0
- data/tests/test_qr.rb +56 -0
- data/tests/test_svd.rb +51 -0
- metadata +108 -0
data/lib/nulin/lls.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
|
2
|
+
module NuLin
|
3
|
+
module_function
|
4
|
+
# Solve the linear least square problem
|
5
|
+
# of min_x |a*x - b|.
|
6
|
+
#
|
7
|
+
# The solution is returned as an instance of NuLin::LLS.
|
8
|
+
#
|
9
|
+
# When n >= m and rank(a) = n, the problem has a unique solution.
|
10
|
+
#
|
11
|
+
# When n < m and rank(a) = n, the equation a*x - b = 0 has
|
12
|
+
# infinitely many solutions. In this case, we consider the problem
|
13
|
+
# of finding the (unique) solution which minimizes |x|.
|
14
|
+
#
|
15
|
+
# If rank(a)=min(m, n), we can find the solution of the two
|
16
|
+
# problems using QR or LQ factorization.
|
17
|
+
#
|
18
|
+
# In the general case, if rank(a) < min(m, n), the above two problems
|
19
|
+
# don't have a unique solution. Therefore we consider the problem
|
20
|
+
# of finding x which minimize |a*x-b| and |x|^2. We can solve the problem
|
21
|
+
# using complete orthogonal factorization or SVD.
|
22
|
+
#
|
23
|
+
# options
|
24
|
+
# * :algorithm (default :qr) - select algorithm :qr, :svd
|
25
|
+
# * :rcond (default -1.0) - The threshold to determine effective rank of
|
26
|
+
# matrix a. This parameter is used only for svd algorithm.
|
27
|
+
# The number of singular values greater than
|
28
|
+
# rcond * (the largest singular value) is regarded as the effective rank.
|
29
|
+
#
|
30
|
+
# Example
|
31
|
+
# # Using QR/LQ algorithm
|
32
|
+
# x = NuLin.linear_least_square(a, b).solution
|
33
|
+
#
|
34
|
+
# # Using SVD
|
35
|
+
# x = NuLin.linear_least_square(a, b, algorithm: :svd, rcond: 0.0001).solution
|
36
|
+
#
|
37
|
+
# @param a[NMatrix] a matrix whose shape is [n, m]
|
38
|
+
# @param b[NVector] a vector whose shape is [m]
|
39
|
+
# @param options[Hash] computation options
|
40
|
+
def linear_least_square(a, b, options={})
|
41
|
+
case options.fetch(:algorithm, :qr)
|
42
|
+
when :qr
|
43
|
+
NuLin::LLS_QR.new(a, b, options)
|
44
|
+
when :svd
|
45
|
+
NuLin::LLS_SVD.new(a, b, options)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def lls(*args); linear_least_square(*args); end
|
50
|
+
end
|
51
|
+
|
52
|
+
class NuLin::LLS
|
53
|
+
def initialize(a, b, options)
|
54
|
+
if !valid_matrix?(a, b)
|
55
|
+
raise NuLin::DimensionError, "Invalid matrix/vector shape"
|
56
|
+
end
|
57
|
+
@a = a.transpose
|
58
|
+
@b_is_rank1 = b.rank == 1
|
59
|
+
@b = (@b_is_rank1) ? b.reshape(b.shape[0], 1) : b
|
60
|
+
|
61
|
+
@typecode = a.typecode
|
62
|
+
end
|
63
|
+
|
64
|
+
def valid_matrix?(a, b)
|
65
|
+
a.rank == 2 &&
|
66
|
+
(b.rank == 1 || b.rank == 2) &&
|
67
|
+
a.shape[1] == b.shape[0] &&
|
68
|
+
a.typecode == b.typecode
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return the solution of the LLS problem as NVector.
|
72
|
+
attr_reader :solution
|
73
|
+
end
|
74
|
+
|
75
|
+
class NuLin::LLS_QR < NuLin::LLS
|
76
|
+
def initialize(a, b, options)
|
77
|
+
super
|
78
|
+
compute
|
79
|
+
end
|
80
|
+
|
81
|
+
def compute
|
82
|
+
m, n = @a.shape
|
83
|
+
lda = m
|
84
|
+
ldb = [1, m, n].max
|
85
|
+
r, nrhs = @b.shape
|
86
|
+
b = NVector.new(@typecode, ldb, nrhs)
|
87
|
+
b[0...m, 0...nrhs] = @b
|
88
|
+
lwork = [1, [m,n].min + [[m,n].min, nrhs].max].max
|
89
|
+
work = NArray.new(@typecode, lwork)
|
90
|
+
NuLin::Native.call(@typecode, "gels", "N", m, n, nrhs, @a, lda, b, ldb,
|
91
|
+
work, lwork, 0)
|
92
|
+
@solution = b[0...n, true]
|
93
|
+
@solution.flatten! if @b_is_rank1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class NuLin::LLS_SVD < NuLin::LLS
|
98
|
+
def initialize(a, b, options)
|
99
|
+
super
|
100
|
+
@rcond = options.fetch(:rcond, -1.0)
|
101
|
+
compute
|
102
|
+
end
|
103
|
+
|
104
|
+
# Singular values of the matrix as NArray.
|
105
|
+
# The values are ordered decreasingly.
|
106
|
+
attr_reader :singular_values
|
107
|
+
# The effective rank of the matrix.
|
108
|
+
attr_reader :rank
|
109
|
+
|
110
|
+
def compute
|
111
|
+
m, n = @a.shape
|
112
|
+
r, nrhs = @b.shape
|
113
|
+
k = [m, n].min
|
114
|
+
lda = [1, m].max
|
115
|
+
ldb = [1, m, n].max
|
116
|
+
b = NVector.new(@typecode, ldb, nrhs)
|
117
|
+
b[0...m, 0...nrhs] = @b
|
118
|
+
s = NArray.new(NuLin.to_real_typecode(@typecode), k)
|
119
|
+
lwork = 3*k + [2*k, m, n, nrhs].max + 1
|
120
|
+
work = NVector.new(@typecode, lwork)
|
121
|
+
if @a.complex?
|
122
|
+
rwork = [NArray.new(@typecode, 5*k)]
|
123
|
+
else
|
124
|
+
rwork = []
|
125
|
+
end
|
126
|
+
@rank, = NuLin::Native.call(@typecode, "gelss", m, n, nrhs, @a, lda, b, ldb, s,
|
127
|
+
@rcond, 0, work, lwork, *rwork, 0)
|
128
|
+
|
129
|
+
@singular_values = s
|
130
|
+
@solution = b[0...n, true]
|
131
|
+
@solution.flatten! if @b_is_rank1
|
132
|
+
end
|
133
|
+
end
|
data/lib/nulin/qr.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
|
2
|
+
module NuLin
|
3
|
+
module_function
|
4
|
+
# Compute the QR Decomposition of `matrix`
|
5
|
+
# and return the result as NuLin::QR object.
|
6
|
+
#
|
7
|
+
# You can call this with `options`, but now the argument is
|
8
|
+
# ignored.
|
9
|
+
#
|
10
|
+
# @param matrix[NMatrix] The matrix
|
11
|
+
# @param options[Hash] computation options, not used now.
|
12
|
+
def qr(matrix, options={})
|
13
|
+
NuLin::QR.new(matrix, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class NuLin::QR
|
18
|
+
def initialize(matrix, options)
|
19
|
+
@matrix = matrix
|
20
|
+
@typecode = matrix.typecode
|
21
|
+
|
22
|
+
compute
|
23
|
+
end
|
24
|
+
|
25
|
+
# The orthgonal/unitary matrix Q
|
26
|
+
attr_reader :Q
|
27
|
+
# The tranposed/adjoint matrix of Q
|
28
|
+
attr_reader :Qt
|
29
|
+
# The upper triangular matrix R
|
30
|
+
attr_reader :R
|
31
|
+
|
32
|
+
private
|
33
|
+
def compute
|
34
|
+
m, n = @matrix.shape
|
35
|
+
lwork = n
|
36
|
+
|
37
|
+
q = base_q
|
38
|
+
tau = NArray.new(@typecode, n)
|
39
|
+
work = NArray.new(@typecode, lwork)
|
40
|
+
|
41
|
+
NuLin::Native.call(@typecode, "gelqf", n, n, q, n, tau, work, lwork, 0)
|
42
|
+
NuLin::Native.call(@typecode, glq_name, n, n, n, q, n, tau, work, lwork, 0)
|
43
|
+
|
44
|
+
@Q = q
|
45
|
+
@Qt = q.adjoint
|
46
|
+
@R = @Qt * @matrix
|
47
|
+
end
|
48
|
+
|
49
|
+
def glq_name
|
50
|
+
if NArray.complex_typecode?(@typecode)
|
51
|
+
"unglq"
|
52
|
+
else
|
53
|
+
"orglq"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def base_q
|
58
|
+
m, n = @matrix.shape
|
59
|
+
case
|
60
|
+
when m >= n
|
61
|
+
@matrix[0...n, 0...n]
|
62
|
+
when m < n
|
63
|
+
bq = NMatrix.new(@typecode, n, n)
|
64
|
+
bq[0...m, 0...n] = @matrix
|
65
|
+
bq
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/nulin/svd.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
module NuLin
|
3
|
+
module_function
|
4
|
+
# Compute the Singular Value Decomposition of `matrix`
|
5
|
+
# and return the result as an instance of SVD
|
6
|
+
#
|
7
|
+
# You can call this with some `options` as follows
|
8
|
+
# * :use_U (default true) Compute the matrix U
|
9
|
+
# * :use_V (default true) Compute the matrix V and V^t
|
10
|
+
# * :full_matrix (default true)
|
11
|
+
# From the theory of SVD,
|
12
|
+
# Any matrix is decomposable into U * sigma * transpose(V)
|
13
|
+
# where
|
14
|
+
# * U, V: orthogonal(real) or unitary(complex) matrices
|
15
|
+
# * sigma: diagonal matrix
|
16
|
+
#
|
17
|
+
# @param matrix[NMatrix] target matrix
|
18
|
+
# @param options[Hash] computation options
|
19
|
+
def svd(matrix, options={})
|
20
|
+
SVD.new(matrix, options)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class NuLin::SVD
|
25
|
+
def initialize(matrix, opts)
|
26
|
+
@matrix = matrix
|
27
|
+
@use_U = opts.fetch(:use_U, true)
|
28
|
+
@use_V = opts.fetch(:use_V, true)
|
29
|
+
@full_matrix = opts.fetch(:full_matrix, true)
|
30
|
+
@typecode = matrix.typecode
|
31
|
+
|
32
|
+
compute
|
33
|
+
end
|
34
|
+
|
35
|
+
# The matrix U
|
36
|
+
attr_reader :U
|
37
|
+
# Transposed matrix of the matrix V
|
38
|
+
attr_reader :Vt
|
39
|
+
# Singular values as NArray object
|
40
|
+
attr_reader :singular_values
|
41
|
+
# The matrix V
|
42
|
+
def V
|
43
|
+
return nil unless @use_V
|
44
|
+
@V ||= @Vt.transpose
|
45
|
+
end
|
46
|
+
# The matrix sigma
|
47
|
+
def sigma
|
48
|
+
return @sigma if @sigma
|
49
|
+
@sigma = NMatrix.new(@matrix.typecode, *@matrix.shape)
|
50
|
+
@sigma.diagonal!(@singular_values.refer)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def compute
|
55
|
+
m, n = @matrix.shape
|
56
|
+
k = [m, n].min
|
57
|
+
l = [m, n].max
|
58
|
+
s = NArray.new(NuLin.to_real_typecode(@typecode), k)
|
59
|
+
ldu = @use_V ? m : 1
|
60
|
+
u = NMatrix.new(@typecode, ldu, @use_V ? (@full_matrix ? m : k) : 0)
|
61
|
+
ldvt = @use_U ? (@full_matrix ? n : k) : 1
|
62
|
+
vt = NMatrix.new(@typecode, ldvt, @use_V ? n : 1)
|
63
|
+
# MAX(1,3*MIN(M,N)+MAX(M,N),5*MIN(M,N)) for real
|
64
|
+
# MAX(1,2*MIN(M,N)+MAX(M,N)) for complx
|
65
|
+
lwork = @matrix.real? ? [1, 3*k+l, 5*k].max : [1, 2*k + l].max
|
66
|
+
work = NArray.new(@typecode, lwork)
|
67
|
+
if @matrix.complex?
|
68
|
+
rwork = [NArray.new(@typecode, 5*k)]
|
69
|
+
else
|
70
|
+
rwork = []
|
71
|
+
end
|
72
|
+
|
73
|
+
NuLin::Native.call(@typecode, "gesvd", job_str(@use_V), job_str(@use_U),
|
74
|
+
m, n, @matrix.dup, m, s, u, ldu, vt, ldvt,
|
75
|
+
work, lwork, *rwork, 0)
|
76
|
+
@U = vt if @use_U
|
77
|
+
@Vt = u if @use_V
|
78
|
+
@singular_values = s
|
79
|
+
end
|
80
|
+
|
81
|
+
def job_str(using)
|
82
|
+
using ? (@full_matrix ? "A" : "S") : "N"
|
83
|
+
end
|
84
|
+
end
|
data/tests/run_test.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test-unit'
|
2
|
+
|
3
|
+
module Test::Unit::Assertions
|
4
|
+
eval <<-EOS
|
5
|
+
def assert_narray_in_delta(expected, actual, delta=0.001, message=nil)
|
6
|
+
failure_message = build_message(message, <<-EOT, expected, delta, actual)
|
7
|
+
<?> +-? expected but was
|
8
|
+
<?>
|
9
|
+
EOT
|
10
|
+
assert_block(failure_message) do
|
11
|
+
((expected - actual).abs < delta).all?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
EOS
|
15
|
+
end
|
16
|
+
|
17
|
+
exit Test::Unit::AutoRunner.run(true, ".")
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'test-unit'
|
2
|
+
require 'nulin'
|
3
|
+
require 'complex'
|
4
|
+
|
5
|
+
class TestNuLin_cholesky < Test::Unit::TestCase
|
6
|
+
def test_cholesky
|
7
|
+
m = NMatrix[ [ 1.18327, -0.484033, -0.675672, -1.33157 ],
|
8
|
+
[ -0.484033, 1.3897, 0.302005, 1.14085 ],
|
9
|
+
[ -0.675672, 0.302005, 0.964183, 0.61185 ],
|
10
|
+
[ -1.33157, 1.14085, 0.61185, 1.84453 ] ]
|
11
|
+
u = NuLin.cholesky(m, type: :U)
|
12
|
+
assert_narray_in_delta(m, u.transpose * u)
|
13
|
+
u.each_with_index do |v, i, j|
|
14
|
+
assert_in_delta(0.0, v) if i < j
|
15
|
+
end
|
16
|
+
|
17
|
+
l = NuLin.cholesky(m, type: :L)
|
18
|
+
assert_narray_in_delta(m, l * l.transpose)
|
19
|
+
l.each_with_index do |v, i, j|
|
20
|
+
assert_in_delta(0.0, v) if i > j
|
21
|
+
end
|
22
|
+
|
23
|
+
m = NMatrix[ [ 5.4452+0.0.i, 1.53215-1.46758.i, 3.47221+1.94952.i ],
|
24
|
+
[ 1.53215+1.46758.i, 8.51107+0.0.i, -5.64531+1.63224.i ],
|
25
|
+
[ 3.47221-1.94952.i, -5.64531-1.63224.i, 8.67626+0.0.i ] ]
|
26
|
+
|
27
|
+
u = NuLin.cholesky(m, type: :U)
|
28
|
+
assert_narray_in_delta(m, u.adjoint * u)
|
29
|
+
u.each_with_index do |v, i, j|
|
30
|
+
assert_in_delta(0.0, v.abs) if i < j
|
31
|
+
end
|
32
|
+
|
33
|
+
l = NuLin.cholesky(m, type: :L)
|
34
|
+
assert_narray_in_delta(m, l * l.adjoint)
|
35
|
+
l.each_with_index do |v, i, j|
|
36
|
+
assert_in_delta(0.0, v.abs) if i > j
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/tests/test_det.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'test-unit'
|
2
|
+
require 'nulin'
|
3
|
+
|
4
|
+
class TestNuLin_eigensystem < Test::Unit::TestCase
|
5
|
+
def test_eigensystem
|
6
|
+
m = NMatrix[[1.2, -4.2, 2.9],
|
7
|
+
[0.92, 1.5, 3.0],
|
8
|
+
[1.0, -0.5, -2.2]]
|
9
|
+
|
10
|
+
check_eigensystem(m, NArray::COMPLEX)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_for_real_eigensystem
|
14
|
+
m = NMatrix[[6.0, -3, -7],
|
15
|
+
[ -1, 2, 1],
|
16
|
+
[ 5, -3, -6]]
|
17
|
+
eig = NuLin.eigensystem(m, use_complex: false)
|
18
|
+
|
19
|
+
assert_equal(3, eig.dim)
|
20
|
+
assert_equal(NArray::FLOAT, eig.eigenvalues.typecode)
|
21
|
+
assert_equal(NArray::FLOAT, eig.right.typecode)
|
22
|
+
assert_equal(NArray::FLOAT, eig.left.typecode)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_eigensystem_sfloat
|
26
|
+
m = NMatrix[[1.2, -4.2, 2.9],
|
27
|
+
[0.92, 1.5, 3.0],
|
28
|
+
[1.0, -0.5, -2.2]]
|
29
|
+
md = NMatrix.sfloat(3, 3)
|
30
|
+
md[] = m
|
31
|
+
|
32
|
+
check_eigensystem(md, NArray::SCOMPLEX)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_eigensystem_complex
|
36
|
+
m = NMatrix[[Complex(1.2, 0.5), Complex(-3.8, -0.9), Complex(1.8)],
|
37
|
+
[Complex(0, -2.3), Complex(1.5, 1.0), Complex(1.2, 1.2)],
|
38
|
+
[Complex(-1.9, 0.1), Complex(0.0), Complex(-4.7, 0.7)]]
|
39
|
+
check_eigensystem(m, NArray::COMPLEX)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_eigensystem_scomplex
|
43
|
+
m = NMatrix[[Complex(1.2, 0.5), Complex(-3.8, -0.9), Complex(1.8)],
|
44
|
+
[Complex(0, -2.3), Complex(1.5, 1.0), Complex(1.2, 1.2)],
|
45
|
+
[Complex(-1.9, 0.1), Complex(0.0), Complex(-4.7, 0.7)]]
|
46
|
+
md = NMatrix.scomplex(3, 3); md[] = m
|
47
|
+
|
48
|
+
check_eigensystem(md, NArray::SCOMPLEX)
|
49
|
+
end
|
50
|
+
|
51
|
+
def check_eigensystem(m, typecode)
|
52
|
+
eig = NuLin.eigensystem(m)
|
53
|
+
|
54
|
+
assert_equal(3, eig.dim)
|
55
|
+
assert_equal(typecode, eig.eigenvalues.typecode)
|
56
|
+
assert_equal(typecode, eig.right.typecode)
|
57
|
+
assert_equal(typecode, eig.left.typecode)
|
58
|
+
|
59
|
+
eig.dim.times do |i|
|
60
|
+
v1 = m * eig.right.column_vector(i)
|
61
|
+
v2 = eig.eigenvalues[i] * eig.right.column_vector(i)
|
62
|
+
assert_narray_in_delta(v1, v2)
|
63
|
+
assert_equal(eig.right.column_vector(i), eig.right_eigenvectors[i])
|
64
|
+
|
65
|
+
w1 = eig.left.row_vector(i) * m
|
66
|
+
w2 = eig.left.row_vector(i) * eig.eigenvalues[i]
|
67
|
+
assert_narray_in_delta(w1, w2)
|
68
|
+
assert_equal(eig.left.row_vector(i), eig.left_eigenvectors[i])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/tests/test_gemm.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'nulin'
|
2
|
+
|
3
|
+
require 'test-unit'
|
4
|
+
|
5
|
+
class TestNuLin_mm < Test::Unit::TestCase
|
6
|
+
def test_mm
|
7
|
+
alpha = 1.4; beta = 0.75
|
8
|
+
a = NMatrix[[0.2, 0, -1.3],
|
9
|
+
[3.1, 0.92, -1.2],
|
10
|
+
[-0.9, 1.0, 0.0],
|
11
|
+
[-1.2, 1.1, 3.1]]
|
12
|
+
b = NMatrix[[0.2, 0.3, -0.123, -0.01, 0.02],
|
13
|
+
[0.32, 0.2, 0.11, -0.12, 1.22],
|
14
|
+
[0.2, 1.2, -0.3, 0.4, 1.1]]
|
15
|
+
c = NMatrix[ [ -0.809611, 0.308441, 0.178653, 0.14375, 1.51829 ],
|
16
|
+
[ -0.438721, -2.22648, 1.19524, -0.767709, 0.0279701 ],
|
17
|
+
[ -0.522194, 0.807863, -0.39172, -0.679458, -0.534994 ],
|
18
|
+
[ -0.0155417, -1.34528, -1.08651, -0.686664, 1.044 ] ]
|
19
|
+
|
20
|
+
assert_narray_in_delta(alpha*a*b + beta*c, NuLin.mm(alpha, a, b, beta, c))
|
21
|
+
|
22
|
+
a = a.to_type(NArray::SFLOAT)
|
23
|
+
b = b.to_type(NArray::SFLOAT)
|
24
|
+
c = c.to_type(NArray::SFLOAT)
|
25
|
+
|
26
|
+
assert_narray_in_delta(alpha*a*b + beta*c, NuLin.mm(alpha, a, b, beta, c))
|
27
|
+
|
28
|
+
alpha = 1.4 + 1.2.i; beta = 0.75 - 0.4.i
|
29
|
+
|
30
|
+
a = NMatrix[ [ 0.125503+0.180057.i, -0.567269+1.70667.i, -1.15548+1.95302.i ],
|
31
|
+
[ 0.59267+0.828465.i, 0.46672-0.0995614.i, -0.635015-1.14206.i ],
|
32
|
+
[ 0.330225+0.122008.i, -0.932912-2.2586.i, -0.13695+1.22169.i ],
|
33
|
+
[ -0.324652+0.629844.i, -0.276656-0.148201.i, -0.919279-1.76697.i ] ]
|
34
|
+
b = NMatrix[ [-0.547 + 0.251.i, -0.202 + 0.206.i, 1.635 + -0.312.i, -0.463 + -0.604.i, -0.440 + 1.257.i],
|
35
|
+
[-0.250 + 0.333.i, -0.220 + 0.140.i, 0.226 + -0.006.i, 1.392 + -0.191.i, 1.113 + 0.915.i],
|
36
|
+
[0.564 + -1.962.i, -0.988 + -0.074.i, 0.395 + -1.089.i, 0.445 + 0.741.i, 0.879 + -1.495.i]]
|
37
|
+
|
38
|
+
c = NMatrix[ [-1.192 + 0.398.i,0.595 + -0.781.i,-0.936 + -2.004.i,-1.110 + -0.091.i,-1.176 + -1.259.i,],
|
39
|
+
[-0.612 + -0.267.i,-0.704 + 2.284.i,-0.816 + -1.802.i,-0.621 + 0.207.i,0.858 + 0.094.i,],
|
40
|
+
[0.185 + 0.021.i,-0.935 + -0.658.i,1.179 + 2.649.i,1.018 + -1.135.i,0.532 + -0.287.i,],
|
41
|
+
[1.378 + 0.254.i,1.047 + 0.024.i,-1.637 + 1.171.i,0.864 + 1.070.i,2.015 + -0.231.i,]]
|
42
|
+
|
43
|
+
assert_narray_in_delta(alpha*a*b + beta*c, NuLin.mm(alpha, a, b, beta, c))
|
44
|
+
assert_narray_in_delta(alpha*a*b + beta*c,
|
45
|
+
NuLin.mm(alpha, a.transpose, b, beta, c, trans_a: :transpose))
|
46
|
+
assert_narray_in_delta(alpha*a*b + beta*c,
|
47
|
+
NuLin.mm(alpha, a.adjoint, b, beta, c, trans_a: :adjoint))
|
48
|
+
assert_narray_in_delta(alpha*a*b + beta*c,
|
49
|
+
NuLin.mm(alpha, a.transpose, b.adjoint, beta, c,
|
50
|
+
trans_a: :transpose, trans_b: :adjoint))
|
51
|
+
a = a.to_type(NArray::SCOMPLEX)
|
52
|
+
b = b.to_type(NArray::SCOMPLEX)
|
53
|
+
c = c.to_type(NArray::SCOMPLEX)
|
54
|
+
|
55
|
+
assert_narray_in_delta(alpha*a*b + beta*c, NuLin.mm(alpha, a, b, beta, c))
|
56
|
+
end
|
57
|
+
end
|