nulin 0.2
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 +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
|