nmatrix-atlas 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.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/ext/nmatrix/data/complex.h +364 -0
  3. data/ext/nmatrix/data/data.h +638 -0
  4. data/ext/nmatrix/data/meta.h +64 -0
  5. data/ext/nmatrix/data/ruby_object.h +389 -0
  6. data/ext/nmatrix/math/asum.h +120 -0
  7. data/ext/nmatrix/math/cblas_enums.h +36 -0
  8. data/ext/nmatrix/math/cblas_templates_core.h +507 -0
  9. data/ext/nmatrix/math/gemm.h +241 -0
  10. data/ext/nmatrix/math/gemv.h +178 -0
  11. data/ext/nmatrix/math/getrf.h +255 -0
  12. data/ext/nmatrix/math/getrs.h +121 -0
  13. data/ext/nmatrix/math/imax.h +79 -0
  14. data/ext/nmatrix/math/laswp.h +165 -0
  15. data/ext/nmatrix/math/long_dtype.h +49 -0
  16. data/ext/nmatrix/math/math.h +744 -0
  17. data/ext/nmatrix/math/nrm2.h +160 -0
  18. data/ext/nmatrix/math/rot.h +117 -0
  19. data/ext/nmatrix/math/rotg.h +106 -0
  20. data/ext/nmatrix/math/scal.h +71 -0
  21. data/ext/nmatrix/math/trsm.h +332 -0
  22. data/ext/nmatrix/math/util.h +148 -0
  23. data/ext/nmatrix/nm_memory.h +60 -0
  24. data/ext/nmatrix/nmatrix.h +408 -0
  25. data/ext/nmatrix/ruby_constants.h +106 -0
  26. data/ext/nmatrix/storage/common.h +176 -0
  27. data/ext/nmatrix/storage/dense/dense.h +128 -0
  28. data/ext/nmatrix/storage/list/list.h +137 -0
  29. data/ext/nmatrix/storage/storage.h +98 -0
  30. data/ext/nmatrix/storage/yale/class.h +1139 -0
  31. data/ext/nmatrix/storage/yale/iterators/base.h +142 -0
  32. data/ext/nmatrix/storage/yale/iterators/iterator.h +130 -0
  33. data/ext/nmatrix/storage/yale/iterators/row.h +449 -0
  34. data/ext/nmatrix/storage/yale/iterators/row_stored.h +139 -0
  35. data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +168 -0
  36. data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +123 -0
  37. data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
  38. data/ext/nmatrix/storage/yale/yale.h +202 -0
  39. data/ext/nmatrix/types.h +54 -0
  40. data/ext/nmatrix/util/io.h +115 -0
  41. data/ext/nmatrix/util/sl_list.h +143 -0
  42. data/ext/nmatrix/util/util.h +78 -0
  43. data/ext/nmatrix_atlas/extconf.rb +250 -0
  44. data/ext/nmatrix_atlas/math_atlas.cpp +1206 -0
  45. data/ext/nmatrix_atlas/math_atlas/cblas_templates_atlas.h +72 -0
  46. data/ext/nmatrix_atlas/math_atlas/clapack_templates.h +332 -0
  47. data/ext/nmatrix_atlas/math_atlas/geev.h +82 -0
  48. data/ext/nmatrix_atlas/math_atlas/gesdd.h +83 -0
  49. data/ext/nmatrix_atlas/math_atlas/gesvd.h +81 -0
  50. data/ext/nmatrix_atlas/math_atlas/inc.h +47 -0
  51. data/ext/nmatrix_atlas/nmatrix_atlas.cpp +44 -0
  52. data/lib/nmatrix/atlas.rb +213 -0
  53. data/lib/nmatrix/lapack_ext_common.rb +69 -0
  54. data/spec/00_nmatrix_spec.rb +730 -0
  55. data/spec/01_enum_spec.rb +190 -0
  56. data/spec/02_slice_spec.rb +389 -0
  57. data/spec/03_nmatrix_monkeys_spec.rb +78 -0
  58. data/spec/2x2_dense_double.mat +0 -0
  59. data/spec/4x4_sparse.mat +0 -0
  60. data/spec/4x5_dense.mat +0 -0
  61. data/spec/blas_spec.rb +193 -0
  62. data/spec/elementwise_spec.rb +303 -0
  63. data/spec/homogeneous_spec.rb +99 -0
  64. data/spec/io/fortran_format_spec.rb +88 -0
  65. data/spec/io/harwell_boeing_spec.rb +98 -0
  66. data/spec/io/test.rua +9 -0
  67. data/spec/io_spec.rb +149 -0
  68. data/spec/lapack_core_spec.rb +482 -0
  69. data/spec/leakcheck.rb +16 -0
  70. data/spec/math_spec.rb +730 -0
  71. data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
  72. data/spec/nmatrix_yale_spec.rb +286 -0
  73. data/spec/plugins/atlas/atlas_spec.rb +242 -0
  74. data/spec/rspec_monkeys.rb +56 -0
  75. data/spec/rspec_spec.rb +34 -0
  76. data/spec/shortcuts_spec.rb +310 -0
  77. data/spec/slice_set_spec.rb +157 -0
  78. data/spec/spec_helper.rb +140 -0
  79. data/spec/stat_spec.rb +203 -0
  80. data/spec/test.pcd +20 -0
  81. data/spec/utm5940.mtx +83844 -0
  82. metadata +159 -0
@@ -0,0 +1,286 @@
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
+ # == nmatrix_yale_spec.rb
24
+ #
25
+ # Basic tests for NMatrix's Yale storage type.
26
+ #
27
+ require 'spec_helper'
28
+ require "./lib/nmatrix"
29
+
30
+ describe NMatrix do
31
+ context :yale do
32
+
33
+ it "compares two empty matrices" do
34
+ n = NMatrix.new(4, stype: :yale, dtype: :float64)
35
+ m = NMatrix.new(4, stype: :yale, dtype: :float64)
36
+ expect(n).to eq(m)
37
+ end
38
+
39
+ it "compares two matrices following basic assignments" do
40
+ n = NMatrix.new(2, stype: :yale, dtype: :float64)
41
+ m = NMatrix.new(2, stype: :yale, dtype: :float64)
42
+
43
+ m[0,0] = 1
44
+ m[0,1] = 1
45
+ expect(n).not_to eq(m)
46
+ n[0,0] = 1
47
+ expect(n).not_to eq(m)
48
+ n[0,1] = 1
49
+ expect(n).to eq(m)
50
+ end
51
+
52
+ it "compares two matrices following elementwise operations" do
53
+ n = NMatrix.new(2, stype: :yale, dtype: :float64)
54
+ m = NMatrix.new(2, stype: :yale, dtype: :float64)
55
+ n[0,1] = 1
56
+ m[0,1] = -1
57
+ x = n+m
58
+ expect(n+m).to eq(NMatrix.new(2, 0.0, stype: :yale))
59
+ end
60
+
61
+ it "sets diagonal values" do
62
+ n = NMatrix.new([2,3], stype: :yale, dtype: :float64)
63
+ n.extend(NMatrix::YaleFunctions)
64
+ n[1,1] = 0.1
65
+ n[0,0] = 0.2
66
+ expect(n.yale_d).to eq([0.2, 0.1])
67
+ end
68
+
69
+ it "gets non-diagonal rows as hashes" do
70
+ n = NMatrix.new([4,6], stype: :yale, dtype: :float64)
71
+ n.extend(NMatrix::YaleFunctions)
72
+ n[0,0] = 0.1
73
+ n[0,2] = 0.2
74
+ n[0,3] = 0.3
75
+ n[1,5] = 0.4
76
+ h = n.yale_nd_row(0, :hash)
77
+ expect(h).to eq({2 => 0.2, 3 => 0.3})
78
+ end
79
+
80
+ it "gets non-diagonal occupied column indices for a given row" do
81
+ n = NMatrix.new([4,6], stype: :yale, dtype: :float64)
82
+ n.extend(NMatrix::YaleFunctions)
83
+ n[0,0] = 0.1
84
+ n[0,2] = 0.2
85
+ n[0,3] = 0.3
86
+ n[1,5] = 0.4
87
+ a = n.yale_nd_row(0, :array)
88
+ expect(a).to eq([2,3])
89
+ end
90
+
91
+ it "does not resize until necessary" do
92
+ n = NMatrix.new([2,3], stype: :yale, dtype: :float64)
93
+ n.extend(NMatrix::YaleFunctions)
94
+ expect(n.yale_size).to eq(3)
95
+ expect(n.capacity).to eq(5)
96
+ n[0,0] = 0.1
97
+ n[0,1] = 0.2
98
+ n[1,0] = 0.3
99
+ expect(n.yale_size).to eq(5)
100
+ expect(n.capacity).to eq(5)
101
+ end
102
+
103
+
104
+ it "sets when not resizing" do
105
+ n = NMatrix.new([2,3], stype: :yale, dtype: :float64)
106
+ n.extend(NMatrix::YaleFunctions)
107
+ n[0,0] = 0.1
108
+ n[0,1] = 0.2
109
+ n[1,0] = 0.3
110
+ expect(n.yale_a).to eq([0.1, 0.0, 0.0, 0.2, 0.3])
111
+ expect(n.yale_ija).to eq([3,4,5,1,0])
112
+ end
113
+
114
+ it "sets when resizing" do
115
+ n = NMatrix.new([2,3], stype: :yale, dtype: :float64)
116
+ n.extend(NMatrix::YaleFunctions)
117
+ n[0,0] = 0.01
118
+ n[1,1] = 0.1
119
+ n[0,1] = 0.2
120
+ n[1,0] = 0.3
121
+ n[1,2] = 0.4
122
+ expect(n.yale_d).to eq([0.01, 0.1])
123
+ expect(n.yale_ia).to eq([3,4,6])
124
+ expect(n.yale_ja).to eq([1,0,2,nil])
125
+ expect(n.yale_lu).to eq([0.2, 0.3, 0.4, nil])
126
+ end
127
+
128
+ it "resizes without erasing values" do
129
+ require 'yaml'
130
+
131
+ associations = File.open('spec/nmatrix_yale_resize_test_associations.yaml') { |y| YAML::load(y) }
132
+
133
+ n = NMatrix.new([618,2801], stype: :yale, dtype: :byte, capacity: associations.size)
134
+ #n = NMatrix.new(:yale, [618, 2801], associations.size, :byte)
135
+
136
+ associations.each_pair do |j,i|
137
+ n[i,j] = 1
138
+ expect(n[i,j]).to be(1), "Value at #{i},#{j} not inserted correctly!"
139
+ end
140
+
141
+ associations.each_pair do |j,i|
142
+ expect(n[i,j]).to be(1), "Value at #{i},#{j} erased during resize!"
143
+ end
144
+ end
145
+
146
+ it "sets values within rows" do
147
+ n = NMatrix.new([3,20], stype: :yale, dtype: :float64)
148
+ n.extend(NMatrix::YaleFunctions)
149
+ n[2,1] = 1.0
150
+ n[2,0] = 1.5
151
+ n[2,15] = 2.0
152
+ expect(n.yale_lu).to eq([1.5, 1.0, 2.0])
153
+ expect(n.yale_ja).to eq([0, 1, 15])
154
+ end
155
+
156
+ it "gets values within rows" do
157
+ n = NMatrix.new([3,20], stype: :yale, dtype: :float64)
158
+ n[2,1] = 1.0
159
+ n[2,0] = 1.5
160
+ n[2,15] = 2.0
161
+ expect(n[2,1]).to eq(1.0)
162
+ expect(n[2,0]).to eq(1.5)
163
+ expect(n[2,15]).to eq(2.0)
164
+ end
165
+
166
+ it "sets values within large rows" do
167
+ n = NMatrix.new([10,300], stype: :yale, dtype: :float64)
168
+ n.extend(NMatrix::YaleFunctions)
169
+ n[5,1] = 1.0
170
+ n[5,0] = 1.5
171
+ n[5,15] = 2.0
172
+ n[5,291] = 3.0
173
+ n[5,292] = 4.0
174
+ n[5,289] = 5.0
175
+ n[5,290] = 6.0
176
+ n[5,293] = 2.0
177
+ n[5,299] = 7.0
178
+ n[5,100] = 8.0
179
+ expect(n.yale_lu).to eq([1.5, 1.0, 2.0, 8.0, 5.0, 6.0, 3.0, 4.0, 2.0, 7.0])
180
+ expect(n.yale_ja).to eq([0, 1, 15, 100, 289, 290, 291, 292, 293, 299])
181
+ end
182
+
183
+ it "gets values within large rows" do
184
+ n = NMatrix.new([10,300], stype: :yale, dtype: :float64)
185
+ n.extend(NMatrix::YaleFunctions)
186
+ n[5,1] = 1.0
187
+ n[5,0] = 1.5
188
+ n[5,15] = 2.0
189
+ n[5,291] = 3.0
190
+ n[5,292] = 4.0
191
+ n[5,289] = 5.0
192
+ n[5,290] = 6.0
193
+ n[5,293] = 2.0
194
+ n[5,299] = 7.0
195
+ n[5,100] = 8.0
196
+
197
+ n.yale_ja.each_index do |idx|
198
+ j = n.yale_ja[idx]
199
+ expect(n[5,j]).to eq(n.yale_lu[idx])
200
+ end
201
+ end
202
+
203
+ it "dots two identical matrices" do
204
+ a = NMatrix.new(4, stype: :yale, dtype: :float64)
205
+ a[0,1] = 4.0
206
+ a[1,2] = 1.0
207
+ a[1,3] = 1.0
208
+ a[3,1] = 2.0
209
+
210
+ b = a.dup
211
+ c = a.dot b
212
+
213
+ d = NMatrix.new(4, [0,0,4,4, 0,2,0,0, 0,0,0,0, 0,0,2,2], dtype: :float64, stype: :yale)
214
+
215
+ expect(c).to eq(d)
216
+ end
217
+
218
+ it "dots two identical matrices where a positive and negative partial sum cancel on the diagonal" do
219
+ a = NMatrix.new(4, 0.0, stype: :yale)
220
+
221
+ a[0,0] = 1.0
222
+ a[0,1] = 4.0
223
+ a[1,2] = 2.0
224
+ a[1,3] = -4.0
225
+ a[3,1] = 4.0
226
+ a[3,3] = 4.0
227
+
228
+ b = a.dup
229
+ c = a.dot b
230
+
231
+ c.extend(NMatrix::YaleFunctions)
232
+
233
+ expect(c.yale_ija.reject { |i| i.nil? }).to eq([5,8,9,9,11,1,2,3,3,1,2])
234
+ expect(c.yale_a.reject { |i| i.nil? }).to eq([1.0, -16.0, 0.0, 0.0, 0.0, 4.0, 8.0, -16.0, -16.0, 16.0, 8.0])
235
+
236
+ end
237
+
238
+ it "dots two vectors" do
239
+ n = NMatrix.new([16,1], 0, stype: :yale)
240
+ m = NMatrix.new([1,16], 0, stype: :yale)
241
+
242
+ n[0] = m[0] = 1
243
+ n[1] = m[1] = 2
244
+ n[2] = m[2] = 3
245
+ n[3] = m[3] = 4
246
+ n[4] = m[4] = 5
247
+ n[5] = m[5] = 6
248
+ n[6] = m[6] = 7
249
+ n[7] = m[7] = 8
250
+ n[8] = m[8] = 9
251
+ n[15] = m[15] = 16
252
+
253
+ nm = n.dot(m)
254
+
255
+ # Perform the same multiplication with dense
256
+ nmr = n.cast(:dense, :int64).dot(m.cast(:dense, :int64)).cast(:yale, :int64)
257
+
258
+ nm.extend(NMatrix::YaleFunctions)
259
+ nmr.extend(NMatrix::YaleFunctions)
260
+
261
+ # We want to do a structure comparison to ensure multiplication is occurring properly, but more importantly, to
262
+ # ensure that insertion sort is occurring as it should. If the row has more than four entries, it'll run quicksort
263
+ # instead. Quicksort calls insertion sort for small rows, so we test both with this particular multiplication.
264
+ expect(nm.yale_ija[0...107]).to eq(nmr.yale_ija[0...107])
265
+ expect(nm.yale_a[0...107]).to eq(nmr.yale_a[0...107])
266
+
267
+ mn = m.dot(n)
268
+ expect(mn[0,0]).to eq(541)
269
+ end
270
+
271
+ it "calculates the row key intersections of two matrices" do
272
+ a = NMatrix.new([3,9], [0,1], stype: :yale, dtype: :byte, default: 0)
273
+ b = NMatrix.new([3,9], [0,0,1,0,1], stype: :yale, dtype: :byte, default: 0)
274
+ a.extend NMatrix::YaleFunctions
275
+ b.extend NMatrix::YaleFunctions
276
+
277
+ (0...3).each do |ai|
278
+ (0...3).each do |bi|
279
+ STDERR.puts (a.yale_ja_d_keys_at(ai) & b.yale_ja_d_keys_at(bi)).inspect
280
+ expect(a.yale_ja_d_keys_at(ai) & b.yale_ja_d_keys_at(bi)).to eq(a.yale_row_keys_intersection(ai, b, bi))
281
+ end
282
+ end
283
+
284
+ end
285
+ end
286
+ end
@@ -0,0 +1,242 @@
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
+ # == atlas_spec.rb
24
+ #
25
+ # Tests for interfaces that are only exposed by nmatrix-atlas
26
+ #
27
+
28
+ require 'spec_helper'
29
+ require "./lib/nmatrix/atlas"
30
+
31
+ describe "NMatrix::LAPACK implementation from nmatrix-atlas plugin" do
32
+ [:float32, :float64, :complex64, :complex128].each do |dtype|
33
+ context dtype do
34
+ it "exposes clapack_getri" do
35
+ a = NMatrix.new(:dense, 3, [1,0,4,1,1,6,-3,0,-10], dtype)
36
+ ipiv = NMatrix::LAPACK::clapack_getrf(:row, 3, 3, a, 3) # get pivot from getrf, use for getri
37
+
38
+ begin
39
+ NMatrix::LAPACK::clapack_getri(:row, 3, a, 3, ipiv)
40
+
41
+ b = NMatrix.new(:dense, 3, [-5,0,-2,-4,1,-1,1.5,0,0.5], dtype)
42
+ expect(a).to eq(b)
43
+ rescue NotImplementedError => e
44
+ pending e.to_s
45
+ end
46
+ end
47
+
48
+ # potrf decomposes a symmetric (or Hermitian)
49
+ # positive-definite matrix. The matrix tested below isn't symmetric.
50
+ # But this is okay since potrf just examines the upper/lower half
51
+ # (as requested) of the matrix and assumes that the rest is symmetric,
52
+ # so we just set the other part of the matrix to zero.
53
+ it "exposes clapack_potrf upper" do
54
+ pending "potrf requires clapack" unless NMatrix.has_clapack?
55
+
56
+ a = NMatrix.new(:dense, 3, [25,15,-5, 0,18,0, 0,0,11], dtype)
57
+ NMatrix::LAPACK::clapack_potrf(:row, :upper, 3, a, 3)
58
+ b = NMatrix.new(:dense, 3, [5,3,-1, 0,3,1, 0,0,3], dtype)
59
+ expect(a).to eq(b)
60
+ end
61
+
62
+ it "exposes clapack_potrf lower" do
63
+ pending "potrf requires clapack" unless NMatrix.has_clapack?
64
+
65
+ a = NMatrix.new(:dense, 3, [25,0,0, 15,18,0,-5,0,11], dtype)
66
+ NMatrix::LAPACK::clapack_potrf(:row, :lower, 3, a, 3)
67
+ b = NMatrix.new(:dense, 3, [5,0,0, 3,3,0, -1,1,3], dtype)
68
+ expect(a).to eq(b)
69
+ end
70
+
71
+ it "exposes clapack_potri" do
72
+ pending "potri requires clapack" unless NMatrix.has_clapack?
73
+
74
+ a = NMatrix.new(3, [4, 0,-1,
75
+ 0, 2, 1,
76
+ 0, 0, 1], dtype: dtype)
77
+ NMatrix::LAPACK::clapack_potrf(:row, :upper, 3, a, 3)
78
+ NMatrix::LAPACK::clapack_potri(:row, :upper, 3, a, 3)
79
+ b = NMatrix.new(3, [0.5, -0.5, 1, 0, 1.5, -2, 0, 0, 4], dtype: dtype)
80
+ err = case dtype
81
+ when :float32, :complex64
82
+ 1e-6
83
+ when :float64, :complex128
84
+ 1e-14
85
+ end
86
+ expect(a).to be_within(err).of(b)
87
+ end
88
+
89
+ it "exposes clapack_potrs" do
90
+ pending "potrs requires clapack" unless NMatrix.has_clapack?
91
+
92
+ a = NMatrix.new(3, [4, 0,-1,
93
+ 0, 2, 1,
94
+ 0, 0, 1], dtype: dtype)
95
+ b = NMatrix.new([3,1], [3,0,2], dtype: dtype)
96
+
97
+ NMatrix::LAPACK::clapack_potrf(:row, :upper, 3, a, 3)
98
+ NMatrix::LAPACK::clapack_potrs(:row, :upper, 3, 1, a, 3, b, 3)
99
+
100
+ x = NMatrix.new([3,1], [3.5, -5.5, 11], dtype: dtype)
101
+
102
+ err = case dtype
103
+ when :float32, :complex64
104
+ 1e-5
105
+ when :float64, :complex128
106
+ 1e-14
107
+ end
108
+
109
+ expect(b).to be_within(err).of(x)
110
+ end
111
+ end
112
+ end
113
+
114
+ [:float32, :float64, :complex64, :complex128].each do |dtype|
115
+ context dtype do
116
+ it "calculates the singular value decomposition with lapack_gesvd" do
117
+ #example from Wikipedia
118
+ m = 4
119
+ n = 5
120
+ mn_min = [m,n].min
121
+ 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)
122
+ s = NMatrix.new([mn_min], 0, dtype: a.abs_dtype) #s is always real and always returned as float/double, never as complex
123
+ u = NMatrix.new([m,m], 0, dtype: dtype)
124
+ vt = NMatrix.new([n,n], 0, dtype: dtype)
125
+
126
+ # This is a pure LAPACK function so it expects column-major functions
127
+ # So we need to transpose the input as well as the output
128
+ a = a.transpose
129
+ NMatrix::LAPACK.lapack_gesvd(:a, :a, m, n, a, m, s, u, m, vt, n, 500)
130
+ u = u.transpose
131
+ vt = vt.transpose
132
+
133
+ s_true = NMatrix.new([mn_min], [4,3,Math.sqrt(5),0], dtype: a.abs_dtype)
134
+ u_true = NMatrix.new([m,m], [0,0,1,0, 0,1,0,0, 0,0,0,-1, 1,0,0,0], dtype: dtype)
135
+ 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)
136
+
137
+ err = case dtype
138
+ when :float32, :complex64
139
+ 1e-5
140
+ when :float64, :complex128
141
+ 1e-14
142
+ end
143
+
144
+ expect(s).to be_within(err).of(s_true)
145
+ expect(u).to be_within(err).of(u_true)
146
+ expect(vt).to be_within(err).of(vt_true)
147
+ end
148
+
149
+ it "calculates the singular value decomposition with lapack_gesdd" do
150
+ #example from Wikipedia
151
+ m = 4
152
+ n = 5
153
+ mn_min = [m,n].min
154
+ 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)
155
+ s = NMatrix.new([mn_min], 0, dtype: a.abs_dtype) #s is always real and always returned as float/double, never as complex
156
+ u = NMatrix.new([m,m], 0, dtype: dtype)
157
+ vt = NMatrix.new([n,n], 0, dtype: dtype)
158
+
159
+ # This is a pure LAPACK function so it expects column-major functions
160
+ # So we need to transpose the input as well as the output
161
+ a = a.transpose
162
+ NMatrix::LAPACK.lapack_gesdd(:a, m, n, a, m, s, u, m, vt, n, 500)
163
+ u = u.transpose
164
+ vt = vt.transpose
165
+
166
+ s_true = NMatrix.new([mn_min], [4,3,Math.sqrt(5),0], dtype: a.abs_dtype)
167
+ u_true = NMatrix.new([m,m], [0,0,1,0, 0,1,0,0, 0,0,0,-1, 1,0,0,0], dtype: dtype)
168
+ 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)
169
+
170
+ err = case dtype
171
+ when :float32, :complex64
172
+ 1e-5
173
+ when :float64, :complex128
174
+ 1e-14
175
+ end
176
+
177
+ expect(s).to be_within(err).of(s_true)
178
+ expect(u).to be_within(err).of(u_true)
179
+ expect(vt).to be_within(err).of(vt_true)
180
+ end
181
+
182
+ it "exposes lapack_geev" do
183
+ n = 3
184
+ a = NMatrix.new([n,n], [-1,0,0, 0,1,-2, 0,1,-1], dtype: dtype)
185
+ w = NMatrix.new([n], dtype: dtype)
186
+ if a.complex_dtype? #for real dtypes, imaginary parts of eigenvalues are stored in separate vector
187
+ wi = nil
188
+ else
189
+ wi = NMatrix.new([n], dtype: dtype)
190
+ end
191
+ vl = NMatrix.new([n,n], dtype: dtype)
192
+ vr = NMatrix.new([n,n], dtype: dtype)
193
+
194
+ # This is a pure LAPACK routine so it expects column-major matrices,
195
+ # so we need to transpose everything.
196
+ a = a.transpose
197
+ NMatrix::LAPACK::lapack_geev(:left, :right, n, a, n, w, wi, vl, n, vr, n, 2*n)
198
+ vr = vr.transpose
199
+ vl = vl.transpose
200
+
201
+ if !a.complex_dtype?
202
+ w = w + wi*Complex(0,1)
203
+ end
204
+
205
+ w_true = NMatrix.new([n], [Complex(0,1), -Complex(0,1), -1], dtype: NMatrix.upcast(dtype, :complex64))
206
+ if a.complex_dtype?
207
+ #For complex types the right/left eigenvectors are stored as columns
208
+ #of vr/vl.
209
+ vr_true = NMatrix.new([n,n],[0,0,1,
210
+ 2/Math.sqrt(6),2/Math.sqrt(6),0,
211
+ Complex(1,-1)/Math.sqrt(6),Complex(1,1)/Math.sqrt(6),0], dtype: dtype)
212
+ vl_true = NMatrix.new([n,n],[0,0,1,
213
+ Complex(-1,1)/Math.sqrt(6),Complex(-1,-1)/Math.sqrt(6),0,
214
+ 2/Math.sqrt(6),2/Math.sqrt(6),0], dtype: dtype)
215
+ else
216
+ #For real types, the real part of the first and second eigenvectors is
217
+ #stored in the first column, the imaginary part of the first (= the
218
+ #negative of the imaginary part of the second) eigenvector is stored
219
+ #in the second column, and the third eigenvector (purely real) is the
220
+ #third column.
221
+ vr_true = NMatrix.new([n,n],[0,0,1,
222
+ 2/Math.sqrt(6),0,0,
223
+ 1/Math.sqrt(6),-1/Math.sqrt(6),0], dtype: dtype)
224
+ vl_true = NMatrix.new([n,n],[0,0,1,
225
+ -1/Math.sqrt(6),1/Math.sqrt(6),0,
226
+ 2/Math.sqrt(6),0,0], dtype: dtype)
227
+ end
228
+
229
+ err = case dtype
230
+ when :float32, :complex64
231
+ 1e-6
232
+ when :float64, :complex128
233
+ 1e-15
234
+ end
235
+
236
+ expect(w).to be_within(err).of(w_true)
237
+ expect(vr).to be_within(err).of(vr_true)
238
+ expect(vl).to be_within(err).of(vl_true)
239
+ end
240
+ end
241
+ end
242
+ end