nmatrix 0.1.0 → 0.2.0
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/ext/nmatrix/data/complex.h +20 -55
- data/ext/nmatrix/data/data.cpp +11 -44
- data/ext/nmatrix/data/data.h +174 -311
- data/ext/nmatrix/data/meta.h +1 -7
- data/ext/nmatrix/data/ruby_object.h +3 -85
- data/ext/nmatrix/extconf.rb +2 -73
- data/ext/nmatrix/math.cpp +170 -813
- data/ext/nmatrix/math/asum.h +2 -25
- data/ext/nmatrix/math/{inc.h → cblas_enums.h} +11 -22
- data/ext/nmatrix/math/cblas_templates_core.h +507 -0
- data/ext/nmatrix/math/gemm.h +2 -32
- data/ext/nmatrix/math/gemv.h +1 -35
- data/ext/nmatrix/math/getrf.h +21 -6
- data/ext/nmatrix/math/getrs.h +0 -8
- data/ext/nmatrix/math/imax.h +0 -22
- data/ext/nmatrix/math/long_dtype.h +0 -3
- data/ext/nmatrix/math/math.h +11 -337
- data/ext/nmatrix/math/nrm2.h +2 -23
- data/ext/nmatrix/math/rot.h +1 -25
- data/ext/nmatrix/math/rotg.h +4 -13
- data/ext/nmatrix/math/scal.h +0 -22
- data/ext/nmatrix/math/trsm.h +0 -55
- data/ext/nmatrix/math/util.h +148 -0
- data/ext/nmatrix/nmatrix.cpp +0 -14
- data/ext/nmatrix/nmatrix.h +92 -84
- data/ext/nmatrix/ruby_constants.cpp +0 -2
- data/ext/nmatrix/ruby_constants.h +0 -2
- data/ext/nmatrix/ruby_nmatrix.c +86 -45
- data/ext/nmatrix/storage/dense/dense.cpp +1 -7
- data/ext/nmatrix/storage/storage.h +0 -1
- data/ext/nmatrix/ttable_helper.rb +0 -6
- data/ext/nmatrix/util/io.cpp +1 -1
- data/lib/nmatrix.rb +1 -19
- data/lib/nmatrix/blas.rb +33 -11
- data/lib/nmatrix/io/market.rb +3 -3
- data/lib/nmatrix/lapack_core.rb +181 -0
- data/lib/nmatrix/lapack_plugin.rb +44 -0
- data/lib/nmatrix/math.rb +382 -131
- data/lib/nmatrix/monkeys.rb +2 -3
- data/lib/nmatrix/nmatrix.rb +166 -13
- data/lib/nmatrix/shortcuts.rb +72 -7
- data/lib/nmatrix/version.rb +2 -2
- data/spec/00_nmatrix_spec.rb +154 -5
- data/spec/02_slice_spec.rb +2 -6
- data/spec/03_nmatrix_monkeys_spec.rb +7 -1
- data/spec/blas_spec.rb +60 -33
- data/spec/homogeneous_spec.rb +10 -10
- data/spec/lapack_core_spec.rb +482 -0
- data/spec/math_spec.rb +436 -52
- data/spec/shortcuts_spec.rb +28 -4
- data/spec/spec_helper.rb +14 -2
- data/spec/utm5940.mtx +83844 -0
- metadata +49 -76
- data/.gitignore +0 -27
- data/.rspec +0 -2
- data/.travis.yml +0 -15
- data/CONTRIBUTING.md +0 -82
- data/Gemfile +0 -2
- data/History.txt +0 -677
- data/LICENSE.txt +0 -23
- data/Manifest.txt +0 -92
- data/README.rdoc +0 -150
- data/Rakefile +0 -216
- data/ext/nmatrix/data/rational.h +0 -440
- data/ext/nmatrix/math/geev.h +0 -82
- data/ext/nmatrix/math/ger.h +0 -96
- data/ext/nmatrix/math/gesdd.h +0 -80
- data/ext/nmatrix/math/gesvd.h +0 -78
- data/ext/nmatrix/math/getf2.h +0 -86
- data/ext/nmatrix/math/getri.h +0 -108
- data/ext/nmatrix/math/potrs.h +0 -129
- data/ext/nmatrix/math/swap.h +0 -52
- data/lib/nmatrix/lapack.rb +0 -240
- data/nmatrix.gemspec +0 -55
- data/scripts/mac-brew-gcc.sh +0 -50
- data/scripts/mac-mavericks-brew-gcc.sh +0 -22
- data/spec/lapack_spec.rb +0 -459
data/spec/02_slice_spec.rb
CHANGED
@@ -258,8 +258,8 @@ describe "Slice operation" do
|
|
258
258
|
end
|
259
259
|
|
260
260
|
if stype == :dense
|
261
|
-
[:byte,:int8,:int16,:int32,:int64,:float32,:float64
|
262
|
-
[:byte,:int8,:int16,:int32,:int64,:float32,:float64
|
261
|
+
[:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |left_dtype|
|
262
|
+
[:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |right_dtype|
|
263
263
|
|
264
264
|
# Won't work if they're both 1-byte, due to overflow.
|
265
265
|
next if [:byte,:int8].include?(left_dtype) && [:byte,:int8].include?(right_dtype)
|
@@ -272,16 +272,12 @@ describe "Slice operation" do
|
|
272
272
|
|
273
273
|
nary = if left_dtype.to_s =~ /complex/
|
274
274
|
COMPLEX_MATRIX43A_ARRAY
|
275
|
-
elsif left_dtype.to_s =~ /rational/
|
276
|
-
RATIONAL_MATRIX43A_ARRAY
|
277
275
|
else
|
278
276
|
MATRIX43A_ARRAY
|
279
277
|
end
|
280
278
|
|
281
279
|
mary = if right_dtype.to_s =~ /complex/
|
282
280
|
COMPLEX_MATRIX32A_ARRAY
|
283
|
-
elsif right_dtype.to_s =~ /rational/
|
284
|
-
RATIONAL_MATRIX32A_ARRAY
|
285
281
|
else
|
286
282
|
MATRIX32A_ARRAY
|
287
283
|
end
|
@@ -59,7 +59,7 @@ describe Array do
|
|
59
59
|
|
60
60
|
it "intuits shape of Array into multiple dimensions" do
|
61
61
|
a = [[[0], [1]], [[2], [3]], [[4], [5]]]
|
62
|
-
expect(a.to_nm).to eq(NMatrix.new([3,1
|
62
|
+
expect(a.to_nm).to eq(NMatrix.new([3,2,1], a.flatten))
|
63
63
|
expect(a).to eq(a.to_nm.to_a)
|
64
64
|
end
|
65
65
|
|
@@ -67,6 +67,12 @@ describe Array do
|
|
67
67
|
a = [[0, 1, 2], [3], [4, 5]]
|
68
68
|
expect(a).to eq(a.to_nm.to_a)
|
69
69
|
end
|
70
|
+
|
71
|
+
it "does not permanently alter the Array" do
|
72
|
+
a = [[0, 1], [2, 3], [4, 5]]
|
73
|
+
expect(a.to_nm).to eq(NMatrix.new([3,2], a.flatten))
|
74
|
+
expect(a).to eq([[0, 1], [2, 3], [4, 5]])
|
75
|
+
end
|
70
76
|
end
|
71
77
|
end
|
72
78
|
|
data/spec/blas_spec.rb
CHANGED
@@ -30,7 +30,6 @@ require 'spec_helper'
|
|
30
30
|
describe NMatrix::BLAS do
|
31
31
|
[:byte, :int8, :int16, :int32, :int64,
|
32
32
|
:float32, :float64, :complex64, :complex128,
|
33
|
-
:rational32, :rational64, :rational128,
|
34
33
|
:object
|
35
34
|
].each do |dtype|
|
36
35
|
context dtype do
|
@@ -48,49 +47,56 @@ describe NMatrix::BLAS do
|
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
51
|
-
[:
|
50
|
+
[:float32, :float64, :complex64, :complex128].each do |dtype|
|
52
51
|
context dtype do
|
53
52
|
# This is not the same as "exposes cblas trsm", which would be for a version defined in blas.rb (which
|
54
53
|
# would greatly simplify the calling of cblas_trsm in terms of arguments, and which would be accessible
|
55
54
|
# as NMatrix::BLAS::trsm)
|
56
55
|
it "exposes unfriendly cblas_trsm" do
|
57
|
-
a = NMatrix.new(3, [4,-1.
|
56
|
+
a = NMatrix.new(3, [4,-1.0/2, -3.0/4, -2, 2, -1.0/4, -4, -2, -1.0/2], dtype: dtype)
|
58
57
|
b = NMatrix.new([3,1], [-1, 17, -9], dtype: dtype)
|
59
58
|
NMatrix::BLAS::cblas_trsm(:row, :right, :lower, :transpose, :nonunit, 1, 3, 1.0, a, 3, b, 3)
|
60
59
|
|
61
60
|
# These test results all come from actually running a matrix through BLAS. We use them to ensure that NMatrix's
|
62
|
-
# version of these functions
|
61
|
+
# version of these functions give similar results.
|
63
62
|
|
64
|
-
expect(b[0]).to eq(-1.
|
65
|
-
expect(b[1]).to eq(33.
|
63
|
+
expect(b[0]).to eq(-1.0/4)
|
64
|
+
expect(b[1]).to eq(33.0/4)
|
66
65
|
expect(b[2]).to eq(-13)
|
67
66
|
|
68
67
|
NMatrix::BLAS::cblas_trsm(:row, :right, :upper, :transpose, :unit, 1, 3, 1.0, a, 3, b, 3)
|
69
68
|
|
70
|
-
expect(b[0]).to eq(-15.
|
69
|
+
expect(b[0]).to eq(-15.0/2)
|
71
70
|
expect(b[1]).to eq(5)
|
72
71
|
expect(b[2]).to eq(-13)
|
73
72
|
end
|
74
|
-
end
|
75
|
-
end
|
76
73
|
|
77
|
-
|
78
|
-
|
79
|
-
it "exposes
|
80
|
-
|
74
|
+
# trmm multiplies two matrices, where one of the two is required to be
|
75
|
+
# triangular
|
76
|
+
it "exposes cblas_trmm" do
|
77
|
+
a = NMatrix.new([3,3], [1,1,1, 0,1,2, 0,0,-1], dtype: dtype)
|
78
|
+
b = NMatrix.new([3,3], [1,2,3, 4,5,6, 7,8,9], dtype: dtype)
|
81
79
|
|
82
|
-
|
83
|
-
|
80
|
+
begin
|
81
|
+
NMatrix::BLAS.cblas_trmm(:row, :left, :upper, false, :not_unit, 3, 3, 1, a, 3, b, 3)
|
82
|
+
rescue NotImplementedError => e
|
83
|
+
pending e.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
product = NMatrix.new([3,3], [12,15,18, 18,21,24, -7,-8,-9], dtype: dtype)
|
87
|
+
expect(b).to eq(product)
|
88
|
+
end
|
84
89
|
end
|
85
90
|
end
|
86
91
|
|
92
|
+
#should have a separate test for complex
|
87
93
|
[:float32, :float64, :complex64, :complex128, :object].each do |dtype|
|
88
94
|
context dtype do
|
89
95
|
|
90
96
|
it "exposes cblas rot" do
|
91
97
|
x = NMatrix.new([5,1], [1,2,3,4,5], dtype: dtype)
|
92
98
|
y = NMatrix.new([5,1], [-5,-4,-3,-2,-1], dtype: dtype)
|
93
|
-
x, y = NMatrix::BLAS::rot(x, y, 1.
|
99
|
+
x, y = NMatrix::BLAS::rot(x, y, 1.0/2, Math.sqrt(3)/2, -1)
|
94
100
|
|
95
101
|
expect(x).to be_within(1e-4).of(
|
96
102
|
NMatrix.new([5,1], [-0.3660254037844386, -0.7320508075688772, -1.098076211353316, -1.4641016151377544, -1.8301270189221928], dtype: dtype)
|
@@ -111,19 +117,23 @@ describe NMatrix::BLAS do
|
|
111
117
|
pending("broken for :object") if dtype == :object
|
112
118
|
|
113
119
|
ab = NMatrix.new([2,1], [6,-8], dtype: dtype)
|
114
|
-
|
120
|
+
begin
|
121
|
+
c,s = NMatrix::BLAS::rotg(ab)
|
122
|
+
rescue NotImplementedError => e
|
123
|
+
pending e.to_s
|
124
|
+
end
|
115
125
|
|
116
126
|
if [:float32, :float64].include?(dtype)
|
117
127
|
expect(ab[0]).to be_within(1e-6).of(-10)
|
118
|
-
expect(ab[1]).to be_within(1e-6).of(-5.
|
119
|
-
expect(c).to be_within(1e-6).of(-3.
|
128
|
+
expect(ab[1]).to be_within(1e-6).of(-5.0/3)
|
129
|
+
expect(c).to be_within(1e-6).of(-3.0/5)
|
120
130
|
else
|
121
131
|
pending "need correct test cases"
|
122
132
|
expect(ab[0]).to be_within(1e-6).of(10)
|
123
|
-
expect(ab[1]).to be_within(1e-6).of(5.
|
124
|
-
expect(c).to be_within(1e-6).of(3.
|
133
|
+
expect(ab[1]).to be_within(1e-6).of(5.0/3)
|
134
|
+
expect(c).to be_within(1e-6).of(3.0/5)
|
125
135
|
end
|
126
|
-
expect(s).to be_within(1e-6).of(4.
|
136
|
+
expect(s).to be_within(1e-6).of(4.0/5)
|
127
137
|
end
|
128
138
|
|
129
139
|
# Note: this exposes gemm, not cblas_gemm (which is the unfriendly CBLAS no-error-checking version)
|
@@ -138,27 +148,44 @@ describe NMatrix::BLAS do
|
|
138
148
|
expect(r).to eq(NMatrix.new([4,2], [273,455,243,235,244,205,102,160], dtype: dtype))
|
139
149
|
end
|
140
150
|
|
141
|
-
|
142
151
|
it "exposes gemv" do
|
143
|
-
a = NMatrix.new([4,3], [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0], dtype:
|
144
|
-
x = NMatrix.new([3,1], [2.0, 1.0, 0.0], dtype:
|
145
|
-
|
146
|
-
NMatrix
|
152
|
+
a = NMatrix.new([4,3], [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0], dtype: dtype)
|
153
|
+
x = NMatrix.new([3,1], [2.0, 1.0, 0.0], dtype: dtype)
|
154
|
+
y = NMatrix::BLAS.gemv(a, x)
|
155
|
+
expect(y).to eq(NMatrix.new([4,1],[4.0,13.0,22.0,31.0],dtype: dtype))
|
147
156
|
end
|
148
157
|
|
149
158
|
it "exposes asum" do
|
150
|
-
|
151
|
-
|
159
|
+
pending("broken for :object") if dtype == :object
|
160
|
+
|
161
|
+
x = NMatrix.new([4,1], [-1,2,3,4], dtype: dtype)
|
162
|
+
expect(NMatrix::BLAS.asum(x)).to eq(10)
|
152
163
|
end
|
153
164
|
|
154
165
|
it "exposes asum for single element" do
|
155
|
-
|
156
|
-
|
166
|
+
if [:complex64,:complex128].include?(dtype)
|
167
|
+
x = NMatrix.new([1], [Complex(-3,2)], dtype: dtype)
|
168
|
+
expect(x.asum).to eq(5.0)
|
169
|
+
else
|
170
|
+
x = NMatrix.new([1], [-1], dtype: dtype)
|
171
|
+
expect(x.asum).to eq(1.0)
|
172
|
+
end
|
157
173
|
end
|
158
174
|
|
159
175
|
it "exposes nrm2" do
|
160
|
-
|
161
|
-
|
176
|
+
pending("broken for :object") if dtype == :object
|
177
|
+
pending("Temporarily disable because the internal implementation of nrm2 is broken -WL 2015-05-17") if dtype == :complex64 || dtype == :complex128
|
178
|
+
|
179
|
+
x = NMatrix.new([4,1], [2,-4,3,5], dtype: dtype)
|
180
|
+
err = case dtype
|
181
|
+
when :float32, :complex64
|
182
|
+
1e-6
|
183
|
+
when :float64, :complex128
|
184
|
+
1e-14
|
185
|
+
else
|
186
|
+
1e-14
|
187
|
+
end
|
188
|
+
expect(NMatrix::BLAS.nrm2(x, 1, 3)).to be_within(err).of(5.385164807134504)
|
162
189
|
end
|
163
190
|
|
164
191
|
end
|
data/spec/homogeneous_spec.rb
CHANGED
@@ -33,10 +33,10 @@ require 'pry'
|
|
33
33
|
describe 'NMatrix' do
|
34
34
|
context ".x_rotation" do
|
35
35
|
it "should generate a matrix representing a rotation about the x axis" do
|
36
|
-
x = NMatrix.x_rotation(Math::PI
|
36
|
+
x = NMatrix.x_rotation(Math::PI/6)
|
37
37
|
expect(x).to be_within(1e-8).of(NMatrix.new([4,4], [1.0, 0.0, 0.0, 0.0,
|
38
|
-
0.0, Math.cos(Math::PI
|
39
|
-
0.0, 0.5, Math.cos(Math::PI
|
38
|
+
0.0, Math.cos(Math::PI/6), -0.5, 0.0,
|
39
|
+
0.0, 0.5, Math.cos(Math::PI/6), 0.0,
|
40
40
|
0.0, 0.0, 0.0, 1.0] ))
|
41
41
|
end
|
42
42
|
end
|
@@ -44,19 +44,19 @@ describe 'NMatrix' do
|
|
44
44
|
|
45
45
|
context ".y_rotation" do
|
46
46
|
it "should generate a matrix representing a rotation about the y axis" do
|
47
|
-
y = NMatrix.y_rotation(Math::PI
|
48
|
-
expect(y).to be_within(1e-8).of(NMatrix.new([4,4], [Math.cos(Math::PI
|
47
|
+
y = NMatrix.y_rotation(Math::PI/6)
|
48
|
+
expect(y).to be_within(1e-8).of(NMatrix.new([4,4], [Math.cos(Math::PI/6), 0.0, 0.5, 0.0,
|
49
49
|
0.0, 1.0, 0.0, 0.0,
|
50
|
-
-0.5, 0.0, Math.cos(Math::PI
|
50
|
+
-0.5, 0.0, Math.cos(Math::PI/6), 0.0,
|
51
51
|
0.0, 0.0, 0.0, 1.0] ))
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
55
|
context ".z_rotation" do
|
56
56
|
it "should generate a matrix representing a rotation about the z axis" do
|
57
|
-
z = NMatrix.z_rotation(Math::PI
|
58
|
-
expect(z).to be_within(1e-8).of(NMatrix.new([4,4], [Math.cos(Math::PI
|
59
|
-
0.5, Math.cos(Math::PI
|
57
|
+
z = NMatrix.z_rotation(Math::PI/6)
|
58
|
+
expect(z).to be_within(1e-8).of(NMatrix.new([4,4], [Math.cos(Math::PI/6), -0.5, 0.0, 0.0,
|
59
|
+
0.5, Math.cos(Math::PI/6), 0.0, 0.0,
|
60
60
|
0.0, 0.0, 1.0, 0.0,
|
61
61
|
0.0, 0.0, 0.0, 1.0] ))
|
62
62
|
end
|
@@ -96,4 +96,4 @@ describe 'NMatrix' do
|
|
96
96
|
expect(Math.sqrt(q[0]**2 + q[1]**2 + q[2]**2 + q[3]**2)).to be_within(1e-6).of(1.0)
|
97
97
|
end
|
98
98
|
end
|
99
|
-
end
|
99
|
+
end
|
@@ -0,0 +1,482 @@
|
|
1
|
+
# = NMatrix
|
2
|
+
#
|
3
|
+
# A linear algebra library for scientific computation in Ruby.
|
4
|
+
# NMatrix is part of SciRuby.
|
5
|
+
#
|
6
|
+
# NMatrix was originally inspired by and derived from NArray, by
|
7
|
+
# Masahiro Tanaka: http://narray.rubyforge.org
|
8
|
+
#
|
9
|
+
# == Copyright Information
|
10
|
+
#
|
11
|
+
# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
|
12
|
+
# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
|
13
|
+
#
|
14
|
+
# Please see LICENSE.txt for additional copyright notices.
|
15
|
+
#
|
16
|
+
# == Contributing
|
17
|
+
#
|
18
|
+
# By contributing source code to SciRuby, you agree to be bound by
|
19
|
+
# our Contributor Agreement:
|
20
|
+
#
|
21
|
+
# * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
|
22
|
+
#
|
23
|
+
# == lapack_core_spec.rb
|
24
|
+
#
|
25
|
+
# Tests for LAPACK functions that have internal implementations (i.e. they
|
26
|
+
# don't rely on external libraries) and also functions that are implemented
|
27
|
+
# by both nmatrix-atlas and nmatrix-lapacke. These tests will also be run for the
|
28
|
+
# plugins that do use external libraries, since they will override the
|
29
|
+
# internal implmentations.
|
30
|
+
#
|
31
|
+
|
32
|
+
require 'spec_helper'
|
33
|
+
|
34
|
+
describe "NMatrix::LAPACK functions with internal implementations" do
|
35
|
+
# where integer math is allowed
|
36
|
+
[:byte, :int8, :int16, :int32, :int64, :float32, :float64, :complex64, :complex128].each do |dtype|
|
37
|
+
context dtype do
|
38
|
+
# This spec seems a little weird. It looks like laswp ignores the last
|
39
|
+
# element of piv, though maybe I misunderstand smth. It would make
|
40
|
+
# more sense if piv were [2,1,3,3]
|
41
|
+
it "exposes clapack laswp" do
|
42
|
+
a = NMatrix.new(:dense, [3,4], [1,2,3,4,5,6,7,8,9,10,11,12], dtype)
|
43
|
+
NMatrix::LAPACK::clapack_laswp(3, a, 4, 0, 3, [2,1,3,0], 1)
|
44
|
+
b = NMatrix.new(:dense, [3,4], [3,2,4,1,7,6,8,5,11,10,12,9], dtype)
|
45
|
+
expect(a).to eq(b)
|
46
|
+
end
|
47
|
+
|
48
|
+
# This spec is OK, because the default behavior for permute_columns
|
49
|
+
# is :intuitive, which is different from :lapack (default laswp behavior)
|
50
|
+
it "exposes NMatrix#permute_columns and #permute_columns! (user-friendly laswp)" do
|
51
|
+
a = NMatrix.new(:dense, [3,4], [1,2,3,4,5,6,7,8,9,10,11,12], dtype)
|
52
|
+
b = NMatrix.new(:dense, [3,4], [3,2,4,1,7,6,8,5,11,10,12,9], dtype)
|
53
|
+
piv = [2,1,3,0]
|
54
|
+
r = a.permute_columns(piv)
|
55
|
+
expect(r).not_to eq(a)
|
56
|
+
expect(r).to eq(b)
|
57
|
+
a.permute_columns!(piv)
|
58
|
+
expect(a).to eq(b)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# where integer math is not allowed
|
64
|
+
[:float32, :float64, :complex64, :complex128].each do |dtype|
|
65
|
+
context dtype do
|
66
|
+
|
67
|
+
# clapack_getrf performs a LU decomposition, but unlike the
|
68
|
+
# standard LAPACK getrf, it's the upper matrix that has unit diagonals
|
69
|
+
# and the permutation is done in columns not rows. See the code for
|
70
|
+
# details.
|
71
|
+
# Also the rows in the pivot vector are indexed starting from 0,
|
72
|
+
# rather than 1 as in LAPACK
|
73
|
+
it "calculates LU decomposition using clapack_getrf (row-major, square)" do
|
74
|
+
a = NMatrix.new(3, [4,9,2,3,5,7,8,1,6], dtype: dtype)
|
75
|
+
ipiv = NMatrix::LAPACK::clapack_getrf(:row, a.shape[0], a.shape[1], a, a.shape[1])
|
76
|
+
b = NMatrix.new(3,[9, 2.0/9, 4.0/9,
|
77
|
+
5, 53.0/9, 7.0/53,
|
78
|
+
1, 52.0/9, 360.0/53], dtype: dtype)
|
79
|
+
ipiv_true = [1,2,2]
|
80
|
+
|
81
|
+
# delta varies for different dtypes
|
82
|
+
err = case dtype
|
83
|
+
when :float32, :complex64
|
84
|
+
1e-6
|
85
|
+
when :float64, :complex128
|
86
|
+
1e-15
|
87
|
+
end
|
88
|
+
|
89
|
+
expect(a).to be_within(err).of(b)
|
90
|
+
expect(ipiv).to eq(ipiv_true)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "calculates LU decomposition using clapack_getrf (row-major, rectangular)" do
|
94
|
+
a = NMatrix.new([3,4], GETRF_EXAMPLE_ARRAY, dtype: dtype)
|
95
|
+
ipiv = NMatrix::LAPACK::clapack_getrf(:row, a.shape[0], a.shape[1], a, a.shape[1])
|
96
|
+
#we can't use GETRF_SOLUTION_ARRAY here, because of the different
|
97
|
+
#conventions of clapack_getrf
|
98
|
+
b = NMatrix.new([3,4],[10.0, -0.1, 0.0, 0.4,
|
99
|
+
3.0, 9.3, 20.0/93, 38.0/93,
|
100
|
+
1.0, 7.1, 602.0/93, 251.0/602], dtype: dtype)
|
101
|
+
ipiv_true = [2,2,2]
|
102
|
+
|
103
|
+
# delta varies for different dtypes
|
104
|
+
err = case dtype
|
105
|
+
when :float32, :complex64
|
106
|
+
1e-6
|
107
|
+
when :float64, :complex128
|
108
|
+
1e-15
|
109
|
+
end
|
110
|
+
|
111
|
+
expect(a).to be_within(err).of(b)
|
112
|
+
expect(ipiv).to eq(ipiv_true)
|
113
|
+
end
|
114
|
+
|
115
|
+
#Normally we wouldn't check column-major routines, since all our matrices
|
116
|
+
#are row-major, but we use the column-major version in #getrf!, so we
|
117
|
+
#want to test it here.
|
118
|
+
it "calculates LU decomposition using clapack_getrf (col-major, rectangular)" do
|
119
|
+
#this is supposed to represent the 3x2 matrix
|
120
|
+
# -1 2
|
121
|
+
# 0 3
|
122
|
+
# 1 -2
|
123
|
+
a = NMatrix.new([1,6], [-1,0,1,2,3,-2], dtype: dtype)
|
124
|
+
ipiv = NMatrix::LAPACK::clapack_getrf(:col, 3, 2, a, 3)
|
125
|
+
b = NMatrix.new([1,6], [-1,0,-1,2,3,0], dtype: dtype)
|
126
|
+
ipiv_true = [0,1]
|
127
|
+
|
128
|
+
# delta varies for different dtypes
|
129
|
+
err = case dtype
|
130
|
+
when :float32, :complex64
|
131
|
+
1e-6
|
132
|
+
when :float64, :complex128
|
133
|
+
1e-15
|
134
|
+
end
|
135
|
+
|
136
|
+
expect(a).to be_within(err).of(b)
|
137
|
+
expect(ipiv).to eq(ipiv_true)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "calculates LU decomposition using #getrf! (rectangular)" do
|
141
|
+
a = NMatrix.new([3,4], GETRF_EXAMPLE_ARRAY, dtype: dtype)
|
142
|
+
ipiv = a.getrf!
|
143
|
+
b = NMatrix.new([3,4], GETRF_SOLUTION_ARRAY, dtype: dtype)
|
144
|
+
ipiv_true = [2,3,3]
|
145
|
+
|
146
|
+
# delta varies for different dtypes
|
147
|
+
err = case dtype
|
148
|
+
when :float32, :complex64
|
149
|
+
1e-6
|
150
|
+
when :float64, :complex128
|
151
|
+
1e-14
|
152
|
+
end
|
153
|
+
|
154
|
+
expect(a).to be_within(err).of(b)
|
155
|
+
expect(ipiv).to eq(ipiv_true)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "calculates LU decomposition using #getrf! (square)" do
|
159
|
+
a = NMatrix.new([4,4], [0,1,2,3, 1,1,1,1, 0,-1,-2,0, 0,2,0,2], dtype: dtype)
|
160
|
+
ipiv = a.getrf!
|
161
|
+
|
162
|
+
b = NMatrix.new([4,4], [1,1,1,1, 0,2,0,2, 0,-0.5,-2,1, 0,0.5,-1,3], dtype: dtype)
|
163
|
+
ipiv_true = [2,4,3,4]
|
164
|
+
|
165
|
+
expect(a).to eq(b)
|
166
|
+
expect(ipiv).to eq(ipiv_true)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Together, these calls are basically xGESV from LAPACK: http://www.netlib.org/lapack/double/dgesv.f
|
170
|
+
it "exposes clapack_getrs" do
|
171
|
+
a = NMatrix.new(3, [-2,4,-3, 3,-2,1, 0,-4,3], dtype: dtype)
|
172
|
+
ipiv = NMatrix::LAPACK::clapack_getrf(:row, 3, 3, a, 3)
|
173
|
+
b = NMatrix.new([3,1], [-1, 17, -9], dtype: dtype)
|
174
|
+
|
175
|
+
NMatrix::LAPACK::clapack_getrs(:row, false, 3, 1, a, 3, ipiv, b, 3)
|
176
|
+
|
177
|
+
expect(b[0]).to eq(5)
|
178
|
+
expect(b[1]).to eq(-15.0/2)
|
179
|
+
expect(b[2]).to eq(-13)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "solves matrix equation (non-vector rhs) using clapack_getrs" do
|
183
|
+
a = NMatrix.new(3, [-2,4,-3, 3,-2,1, 0,-4,3], dtype: dtype)
|
184
|
+
b = NMatrix.new([3,2], [-1,2, 17,1, -9,-4], dtype: dtype)
|
185
|
+
|
186
|
+
n = a.shape[0]
|
187
|
+
nrhs = b.shape[1]
|
188
|
+
|
189
|
+
ipiv = NMatrix::LAPACK::clapack_getrf(:row, n, n, a, n)
|
190
|
+
# Even though we pass :row to clapack_getrs, it still interprets b as
|
191
|
+
# column-major, so need to transpose b before and after:
|
192
|
+
b = b.transpose
|
193
|
+
NMatrix::LAPACK::clapack_getrs(:row, false, n, nrhs, a, n, ipiv, b, n)
|
194
|
+
b = b.transpose
|
195
|
+
|
196
|
+
b_true = NMatrix.new([3,2], [5,1, -7.5,1, -13,0], dtype: dtype)
|
197
|
+
expect(b).to eq(b_true)
|
198
|
+
end
|
199
|
+
|
200
|
+
#posv is like potrf+potrs
|
201
|
+
#posv is implemented in both nmatrix-atlas and nmatrix-lapacke, so the spec
|
202
|
+
#needs to be shared here
|
203
|
+
it "solves a (symmetric positive-definite) matrix equation using posv (vector rhs)" do
|
204
|
+
a = NMatrix.new(3, [4, 0,-1,
|
205
|
+
0, 2, 1,
|
206
|
+
0, 0, 1], dtype: dtype)
|
207
|
+
b = NMatrix.new([3,1], [4,2,0], dtype: dtype)
|
208
|
+
|
209
|
+
begin
|
210
|
+
x = NMatrix::LAPACK::posv(:upper, a, b)
|
211
|
+
rescue NotImplementedError => e
|
212
|
+
pending e.to_s
|
213
|
+
end
|
214
|
+
|
215
|
+
x_true = NMatrix.new([3,1], [1, 1, 0], dtype: dtype)
|
216
|
+
|
217
|
+
err = case dtype
|
218
|
+
when :float32, :complex64
|
219
|
+
1e-5
|
220
|
+
when :float64, :complex128
|
221
|
+
1e-14
|
222
|
+
end
|
223
|
+
|
224
|
+
expect(x).to be_within(err).of(x_true)
|
225
|
+
end
|
226
|
+
|
227
|
+
it "solves a (symmetric positive-definite) matrix equation using posv (non-vector rhs)" do
|
228
|
+
a = NMatrix.new(3, [4, 0,-1,
|
229
|
+
0, 2, 1,
|
230
|
+
0, 0, 1], dtype: dtype)
|
231
|
+
b = NMatrix.new([3,2], [4,-1, 2,-1, 0,0], dtype: dtype)
|
232
|
+
|
233
|
+
begin
|
234
|
+
x = NMatrix::LAPACK::posv(:upper, a, b)
|
235
|
+
rescue NotImplementedError => e
|
236
|
+
pending e.to_s
|
237
|
+
end
|
238
|
+
|
239
|
+
x_true = NMatrix.new([3,2], [1,0, 1,-1, 0,1], dtype: dtype)
|
240
|
+
|
241
|
+
err = case dtype
|
242
|
+
when :float32, :complex64
|
243
|
+
1e-5
|
244
|
+
when :float64, :complex128
|
245
|
+
1e-14
|
246
|
+
end
|
247
|
+
|
248
|
+
expect(x).to be_within(err).of(x_true)
|
249
|
+
end
|
250
|
+
|
251
|
+
it "calculates the singular value decomposition with NMatrix#gesvd" do
|
252
|
+
#example from Wikipedia
|
253
|
+
m = 4
|
254
|
+
n = 5
|
255
|
+
mn_min = [m,n].min
|
256
|
+
a = NMatrix.new([m,n],[1,0,0,0,2, 0,0,3,0,0, 0,0,0,0,0, 0,4,0,0,0], dtype: dtype)
|
257
|
+
|
258
|
+
begin
|
259
|
+
u, s, vt = a.gesvd
|
260
|
+
rescue NotImplementedError => e
|
261
|
+
pending e.to_s
|
262
|
+
end
|
263
|
+
|
264
|
+
s_true = NMatrix.new([mn_min,1], [4,3,Math.sqrt(5),0], dtype: a.abs_dtype)
|
265
|
+
u_true = NMatrix.new([m,m], [0,0,1,0, 0,1,0,0, 0,0,0,-1, 1,0,0,0], dtype: dtype)
|
266
|
+
vt_true = NMatrix.new([n,n], [0,1,0,0,0, 0,0,1,0,0, Math.sqrt(0.2),0,0,0,Math.sqrt(0.8), 0,0,0,1,0, -Math.sqrt(0.8),0,0,0,Math.sqrt(0.2)], dtype: dtype)
|
267
|
+
|
268
|
+
err = case dtype
|
269
|
+
when :float32, :complex64
|
270
|
+
1e-5
|
271
|
+
when :float64, :complex128
|
272
|
+
1e-14
|
273
|
+
end
|
274
|
+
|
275
|
+
expect(s).to be_within(err).of(s_true)
|
276
|
+
expect(u).to be_within(err).of(u_true)
|
277
|
+
expect(vt).to be_within(err).of(vt_true)
|
278
|
+
|
279
|
+
expect(s.dtype).to eq(a.abs_dtype)
|
280
|
+
expect(u.dtype).to eq(dtype)
|
281
|
+
expect(vt.dtype).to eq(dtype)
|
282
|
+
end
|
283
|
+
|
284
|
+
it "calculates the singular value decomposition with NMatrix#gesdd" do
|
285
|
+
#example from Wikipedia
|
286
|
+
m = 4
|
287
|
+
n = 5
|
288
|
+
mn_min = [m,n].min
|
289
|
+
a = NMatrix.new([m,n],[1,0,0,0,2, 0,0,3,0,0, 0,0,0,0,0, 0,4,0,0,0], dtype: dtype)
|
290
|
+
|
291
|
+
begin
|
292
|
+
u, s, vt = a.gesdd
|
293
|
+
rescue NotImplementedError => e
|
294
|
+
pending e.to_s
|
295
|
+
end
|
296
|
+
|
297
|
+
s_true = NMatrix.new([mn_min,1], [4,3,Math.sqrt(5),0], dtype: a.abs_dtype)
|
298
|
+
u_true = NMatrix.new([m,m], [0,0,1,0, 0,1,0,0, 0,0,0,-1, 1,0,0,0], dtype: dtype)
|
299
|
+
vt_true = NMatrix.new([n,n], [0,1,0,0,0, 0,0,1,0,0, Math.sqrt(0.2),0,0,0,Math.sqrt(0.8), 0,0,0,1,0, -Math.sqrt(0.8),0,0,0,Math.sqrt(0.2)], dtype: dtype)
|
300
|
+
|
301
|
+
err = case dtype
|
302
|
+
when :float32, :complex64
|
303
|
+
1e-5
|
304
|
+
when :float64, :complex128
|
305
|
+
1e-14
|
306
|
+
end
|
307
|
+
|
308
|
+
expect(s).to be_within(err).of(s_true)
|
309
|
+
expect(u).to be_within(err).of(u_true)
|
310
|
+
expect(vt).to be_within(err).of(vt_true)
|
311
|
+
end
|
312
|
+
|
313
|
+
|
314
|
+
it "calculates eigenvalues and eigenvectors NMatrix::LAPACK.geev (real matrix, complex eigenvalues)" do
|
315
|
+
n = 3
|
316
|
+
a = NMatrix.new([n,n], [-1,0,0, 0,1,-2, 0,1,-1], dtype: dtype)
|
317
|
+
|
318
|
+
begin
|
319
|
+
eigenvalues, vl, vr = NMatrix::LAPACK.geev(a)
|
320
|
+
rescue NotImplementedError => e
|
321
|
+
pending e.to_s
|
322
|
+
end
|
323
|
+
|
324
|
+
eigenvalues_true = NMatrix.new([n,1], [Complex(0,1), -Complex(0,1), -1], dtype: NMatrix.upcast(dtype, :complex64))
|
325
|
+
vr_true = NMatrix.new([n,n],[0,0,1,
|
326
|
+
2/Math.sqrt(6),2/Math.sqrt(6),0,
|
327
|
+
Complex(1,-1)/Math.sqrt(6),Complex(1,1)/Math.sqrt(6),0], dtype: NMatrix.upcast(dtype, :complex64))
|
328
|
+
vl_true = NMatrix.new([n,n],[0,0,1,
|
329
|
+
Complex(-1,1)/Math.sqrt(6),Complex(-1,-1)/Math.sqrt(6),0,
|
330
|
+
2/Math.sqrt(6),2/Math.sqrt(6),0], dtype: NMatrix.upcast(dtype, :complex64))
|
331
|
+
|
332
|
+
err = case dtype
|
333
|
+
when :float32, :complex64
|
334
|
+
1e-6
|
335
|
+
when :float64, :complex128
|
336
|
+
1e-15
|
337
|
+
end
|
338
|
+
|
339
|
+
expect(eigenvalues).to be_within(err).of(eigenvalues_true)
|
340
|
+
expect(vr).to be_within(err).of(vr_true)
|
341
|
+
expect(vl).to be_within(err).of(vl_true)
|
342
|
+
|
343
|
+
expect(eigenvalues.dtype).to eq(NMatrix.upcast(dtype, :complex64))
|
344
|
+
expect(vr.dtype).to eq(NMatrix.upcast(dtype, :complex64))
|
345
|
+
expect(vl.dtype).to eq(NMatrix.upcast(dtype, :complex64))
|
346
|
+
end
|
347
|
+
|
348
|
+
it "calculates eigenvalues and eigenvectors NMatrix::LAPACK.geev (real matrix, real eigenvalues)" do
|
349
|
+
n = 3
|
350
|
+
a = NMatrix.new([n,n], [2,0,0, 0,3,2, 0,1,2], dtype: dtype)
|
351
|
+
|
352
|
+
begin
|
353
|
+
eigenvalues, vl, vr = NMatrix::LAPACK.geev(a)
|
354
|
+
rescue NotImplementedError => e
|
355
|
+
pending e.to_s
|
356
|
+
end
|
357
|
+
|
358
|
+
eigenvalues_true = NMatrix.new([n,1], [1, 4, 2], dtype: dtype)
|
359
|
+
|
360
|
+
# For some reason, some of the eigenvectors have different signs
|
361
|
+
# when we use the complex versions of geev. This is totally fine, since
|
362
|
+
# they are still normalized eigenvectors even with the sign flipped.
|
363
|
+
if a.complex_dtype?
|
364
|
+
vr_true = NMatrix.new([n,n],[0,0,1,
|
365
|
+
1/Math.sqrt(2),2/Math.sqrt(5),0,
|
366
|
+
-1/Math.sqrt(2),1/Math.sqrt(5),0], dtype: dtype)
|
367
|
+
vl_true = NMatrix.new([n,n],[0,0,1,
|
368
|
+
-1/Math.sqrt(5),1/Math.sqrt(2),0,
|
369
|
+
2/Math.sqrt(5),1/Math.sqrt(2),0], dtype: dtype)
|
370
|
+
else
|
371
|
+
vr_true = NMatrix.new([n,n],[0,0,1,
|
372
|
+
1/Math.sqrt(2),-2/Math.sqrt(5),0,
|
373
|
+
-1/Math.sqrt(2),-1/Math.sqrt(5),0], dtype: dtype)
|
374
|
+
vl_true = NMatrix.new([n,n],[0,0,1,
|
375
|
+
1/Math.sqrt(5),-1/Math.sqrt(2),0,
|
376
|
+
-2/Math.sqrt(5),-1/Math.sqrt(2),0], dtype: dtype)
|
377
|
+
end
|
378
|
+
|
379
|
+
err = case dtype
|
380
|
+
when :float32, :complex64
|
381
|
+
1e-6
|
382
|
+
when :float64, :complex128
|
383
|
+
1e-15
|
384
|
+
end
|
385
|
+
|
386
|
+
expect(eigenvalues).to be_within(err).of(eigenvalues_true)
|
387
|
+
expect(vr).to be_within(err).of(vr_true)
|
388
|
+
expect(vl).to be_within(err).of(vl_true)
|
389
|
+
|
390
|
+
expect(eigenvalues.dtype).to eq(dtype)
|
391
|
+
expect(vr.dtype).to eq(dtype)
|
392
|
+
expect(vl.dtype).to eq(dtype)
|
393
|
+
end
|
394
|
+
|
395
|
+
it "calculates eigenvalues and eigenvectors NMatrix::LAPACK.geev (left eigenvectors only)" do
|
396
|
+
n = 3
|
397
|
+
a = NMatrix.new([n,n], [-1,0,0, 0,1,-2, 0,1,-1], dtype: dtype)
|
398
|
+
|
399
|
+
begin
|
400
|
+
eigenvalues, vl = NMatrix::LAPACK.geev(a, :left)
|
401
|
+
rescue NotImplementedError => e
|
402
|
+
pending e.to_s
|
403
|
+
end
|
404
|
+
|
405
|
+
eigenvalues_true = NMatrix.new([n,1], [Complex(0,1), -Complex(0,1), -1], dtype: NMatrix.upcast(dtype, :complex64))
|
406
|
+
vl_true = NMatrix.new([n,n],[0,0,1,
|
407
|
+
Complex(-1,1)/Math.sqrt(6),Complex(-1,-1)/Math.sqrt(6),0,
|
408
|
+
2/Math.sqrt(6),2/Math.sqrt(6),0], dtype: NMatrix.upcast(dtype, :complex64))
|
409
|
+
|
410
|
+
err = case dtype
|
411
|
+
when :float32, :complex64
|
412
|
+
1e-6
|
413
|
+
when :float64, :complex128
|
414
|
+
1e-15
|
415
|
+
end
|
416
|
+
|
417
|
+
expect(eigenvalues).to be_within(err).of(eigenvalues_true)
|
418
|
+
expect(vl).to be_within(err).of(vl_true)
|
419
|
+
end
|
420
|
+
|
421
|
+
it "calculates eigenvalues and eigenvectors NMatrix::LAPACK.geev (right eigenvectors only)" do
|
422
|
+
n = 3
|
423
|
+
a = NMatrix.new([n,n], [-1,0,0, 0,1,-2, 0,1,-1], dtype: dtype)
|
424
|
+
|
425
|
+
begin
|
426
|
+
eigenvalues, vr = NMatrix::LAPACK.geev(a, :right)
|
427
|
+
rescue NotImplementedError => e
|
428
|
+
pending e.to_s
|
429
|
+
end
|
430
|
+
|
431
|
+
eigenvalues_true = NMatrix.new([n,1], [Complex(0,1), -Complex(0,1), -1], dtype: NMatrix.upcast(dtype, :complex64))
|
432
|
+
vr_true = NMatrix.new([n,n],[0,0,1,
|
433
|
+
2/Math.sqrt(6),2/Math.sqrt(6),0,
|
434
|
+
Complex(1,-1)/Math.sqrt(6),Complex(1,1)/Math.sqrt(6),0], dtype: NMatrix.upcast(dtype, :complex64))
|
435
|
+
|
436
|
+
err = case dtype
|
437
|
+
when :float32, :complex64
|
438
|
+
1e-6
|
439
|
+
when :float64, :complex128
|
440
|
+
1e-15
|
441
|
+
end
|
442
|
+
|
443
|
+
expect(eigenvalues).to be_within(err).of(eigenvalues_true)
|
444
|
+
expect(vr).to be_within(err).of(vr_true)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
[:complex64, :complex128].each do |dtype|
|
450
|
+
context dtype do
|
451
|
+
it "calculates eigenvalues and eigenvectors NMatrix::LAPACK.geev (complex matrix)" do
|
452
|
+
n = 3
|
453
|
+
a = NMatrix.new([n,n], [Complex(0,1),0,0, 0,3,2, 0,1,2], dtype: dtype)
|
454
|
+
|
455
|
+
begin
|
456
|
+
eigenvalues, vl, vr = NMatrix::LAPACK.geev(a)
|
457
|
+
rescue NotImplementedError => e
|
458
|
+
pending e.to_s
|
459
|
+
end
|
460
|
+
|
461
|
+
eigenvalues_true = NMatrix.new([n,1], [1, 4, Complex(0,1)], dtype: dtype)
|
462
|
+
vr_true = NMatrix.new([n,n],[0,0,1,
|
463
|
+
1/Math.sqrt(2),2/Math.sqrt(5),0,
|
464
|
+
-1/Math.sqrt(2),1/Math.sqrt(5),0], dtype: dtype)
|
465
|
+
vl_true = NMatrix.new([n,n],[0,0,1,
|
466
|
+
-1/Math.sqrt(5),1/Math.sqrt(2),0,
|
467
|
+
2/Math.sqrt(5),1/Math.sqrt(2),0], dtype: dtype)
|
468
|
+
|
469
|
+
err = case dtype
|
470
|
+
when :float32, :complex64
|
471
|
+
1e-6
|
472
|
+
when :float64, :complex128
|
473
|
+
1e-15
|
474
|
+
end
|
475
|
+
|
476
|
+
expect(eigenvalues).to be_within(err).of(eigenvalues_true)
|
477
|
+
expect(vr).to be_within(err).of(vr_true)
|
478
|
+
expect(vl).to be_within(err).of(vl_true)
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|