nmatrix 0.0.1

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 (43) hide show
  1. data/.autotest +23 -0
  2. data/.gemtest +0 -0
  3. data/Gemfile +7 -0
  4. data/History.txt +6 -0
  5. data/LICENSE.txt +21 -0
  6. data/Manifest.txt +51 -0
  7. data/README.rdoc +63 -0
  8. data/Rakefile +154 -0
  9. data/ext/nmatrix/cblas.c +150 -0
  10. data/ext/nmatrix/dense.c +307 -0
  11. data/ext/nmatrix/dense/blas_header.template.c +52 -0
  12. data/ext/nmatrix/dense/elementwise.template.c +107 -0
  13. data/ext/nmatrix/dense/gemm.template.c +159 -0
  14. data/ext/nmatrix/dense/gemv.template.c +130 -0
  15. data/ext/nmatrix/dense/rationalmath.template.c +68 -0
  16. data/ext/nmatrix/depend +18 -0
  17. data/ext/nmatrix/extconf.rb +143 -0
  18. data/ext/nmatrix/generator.rb +594 -0
  19. data/ext/nmatrix/generator/syntax_tree.rb +481 -0
  20. data/ext/nmatrix/list.c +774 -0
  21. data/ext/nmatrix/nmatrix.c +1977 -0
  22. data/ext/nmatrix/nmatrix.h +912 -0
  23. data/ext/nmatrix/rational.c +98 -0
  24. data/ext/nmatrix/yale.c +726 -0
  25. data/ext/nmatrix/yale/complexmath.template.c +71 -0
  26. data/ext/nmatrix/yale/elementwise.template.c +46 -0
  27. data/ext/nmatrix/yale/elementwise_op.template.c +73 -0
  28. data/ext/nmatrix/yale/numbmm.template.c +94 -0
  29. data/ext/nmatrix/yale/smmp1.template.c +21 -0
  30. data/ext/nmatrix/yale/smmp1_header.template.c +38 -0
  31. data/ext/nmatrix/yale/smmp2.template.c +43 -0
  32. data/ext/nmatrix/yale/smmp2_header.template.c +46 -0
  33. data/ext/nmatrix/yale/sort_columns.template.c +56 -0
  34. data/ext/nmatrix/yale/symbmm.template.c +54 -0
  35. data/ext/nmatrix/yale/transp.template.c +68 -0
  36. data/lib/array.rb +67 -0
  37. data/lib/nmatrix.rb +263 -0
  38. data/lib/string.rb +65 -0
  39. data/spec/nmatrix_spec.rb +395 -0
  40. data/spec/nmatrix_yale_spec.rb +239 -0
  41. data/spec/nvector_spec.rb +43 -0
  42. data/spec/syntax_tree_spec.rb +46 -0
  43. metadata +150 -0
@@ -0,0 +1,65 @@
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 - 2012, Ruby Science Foundation
12
+ # NMatrix is Copyright (c) 2012, 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
+ # == string.rb
24
+ #
25
+ # Ruby core extensions for NMatrix.
26
+
27
+ class String #:nodoc:
28
+ unless method_defined?(:constantize)
29
+ # Based on constantize from ActiveSupport::Inflector
30
+ def constantize
31
+ names = self.split('::')
32
+ names.shift if names.empty? || names.first.empty?
33
+
34
+ constant = Object
35
+ names.each do |name|
36
+ constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name)
37
+ end
38
+ constant
39
+ end
40
+ end
41
+
42
+ unless method_defined?(:camelize)
43
+ # Adapted from camelize from ActiveSupport::Inflector
44
+ def camelize first_letter_in_uppercase = true
45
+ if first_letter_in_uppercase
46
+ self.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
47
+ else
48
+ self.to_s[0].chr.downcase + self[1..-1].camelize
49
+ end
50
+ end
51
+ end
52
+
53
+ unless method_defined?(:underscore)
54
+ # Adapted from underscore from ActiveSupport::Inflector
55
+ def underscore
56
+ word = self.dup
57
+ word.gsub!(/::/, '/')
58
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
59
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
60
+ word.tr!("-", "_")
61
+ word.downcase!
62
+ word
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,395 @@
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 - 2012, Ruby Science Foundation
12
+ # NMatrix is Copyright (c) 2012, 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_spec.rb
24
+ #
25
+ # Basic tests for NMatrix.
26
+ #
27
+ require "./lib/nmatrix"
28
+
29
+ describe NMatrix do
30
+ MATRIX43A_ARRAY = [14.0, 9.0, 3.0, 2.0, 11.0, 15.0, 0.0, 12.0, 17.0, 5.0, 2.0, 3.0]
31
+ MATRIX32A_ARRAY = [12.0, 25.0, 9.0, 10.0, 8.0, 5.0]
32
+
33
+ COMPLEX_MATRIX43A_ARRAY = MATRIX43A_ARRAY.zip(MATRIX43A_ARRAY.reverse).collect { |ary| Complex(ary[0], ary[1]) }
34
+ COMPLEX_MATRIX32A_ARRAY = MATRIX32A_ARRAY.zip(MATRIX32A_ARRAY.reverse).collect { |ary| Complex(ary[0], -ary[1]) }
35
+
36
+ RATIONAL_MATRIX43A_ARRAY = MATRIX43A_ARRAY.collect { |x| x.to_r }
37
+ RATIONAL_MATRIX32A_ARRAY = MATRIX32A_ARRAY.collect { |x| x.to_r }
38
+
39
+
40
+ it "allows stype casting of a rank 2 matrix between dense, sparse, and list (different dtypes)" do
41
+ m = NMatrix.new(:dense, [3,3], [0,0,1,0,2,0,3,4,5], :int64).
42
+ cast(:yale, :int32).
43
+ cast(:dense, :float64).
44
+ cast(:list, :int32).
45
+ cast(:dense, :int16).
46
+ cast(:list, :int32).
47
+ cast(:yale, :int64) #.
48
+ #cast(:list, :int32).
49
+ #cast(:dense, :int16)
50
+ #m.should.equal?(original)
51
+ # For some reason this causes some weird garbage collector problems when we uncomment these. The above lines won't
52
+ # work at all in IRB, but work fine when run in a regular Ruby session.
53
+ end
54
+
55
+
56
+ it "correctly compares two list matrices" do
57
+ n = NMatrix.new(:list, [3,3,3], :int64)
58
+ m = NMatrix.new(:list, [3,3,3], :int64)
59
+ n.should.eql? m
60
+ n[0,0,0] = 5
61
+ n.should_not.eql? m
62
+ n[0,0,1] = 52
63
+ n[1,2,1] = -4
64
+
65
+ m[0,0,0] = 5
66
+ m[0,0,1] = 52
67
+ m[1,2,1] = -4
68
+ n.should.eql? m
69
+ end
70
+
71
+ it "correctly fills dense Ruby object matrix with nil" do
72
+ n = NMatrix.new([4,3], :object)
73
+ n[0,0].should == nil
74
+ end
75
+
76
+ it "correctly fills dense with individual assignments" do
77
+ n = NMatrix.new([4,3], :float64)
78
+ n[0,0] = 14.0
79
+ n[0,1] = 9.0
80
+ n[0,2] = 3.0
81
+ n[1,0] = 2.0
82
+ n[1,1] = 11.0
83
+ n[1,2] = 15.0
84
+ n[2,0] = 0.0
85
+ n[2,1] = 12.0
86
+ n[2,2] = 17.0
87
+ n[3,0] = 5.0
88
+ n[3,1] = 2.0
89
+ n[3,2] = 3.0
90
+
91
+ n[0,0].should == 14.0
92
+ n[0,1].should == 9.0
93
+ n[0,2].should == 3.0
94
+ n[1,0].should == 2.0
95
+ n[1,1].should == 11.0
96
+ n[1,2].should == 15.0
97
+ n[2,0].should == 0.0
98
+ n[2,1].should == 12.0
99
+ n[2,2].should == 17.0
100
+ n[3,0].should == 5.0
101
+ n[3,1].should == 2.0
102
+ n[3,2].should == 3.0
103
+ end
104
+
105
+ it "correctly fills dense with a single mass assignment" do
106
+ n = NMatrix.new([4,3], [14.0, 9.0, 3.0, 2.0, 11.0, 15.0, 0.0, 12.0, 17.0, 5.0, 2.0, 3.0])
107
+
108
+ n[0,0].should == 14.0
109
+ n[0,1].should == 9.0
110
+ n[0,2].should == 3.0
111
+ n[1,0].should == 2.0
112
+ n[1,1].should == 11.0
113
+ n[1,2].should == 15.0
114
+ n[2,0].should == 0.0
115
+ n[2,1].should == 12.0
116
+ n[2,2].should == 17.0
117
+ n[3,0].should == 5.0
118
+ n[3,1].should == 2.0
119
+ n[3,2].should == 3.0
120
+ end
121
+
122
+ it "correctly fills dense with a single mass assignment, with dtype specified" do
123
+ m = NMatrix.new([4,3], [14.0, 9.0, 3.0, 2.0, 11.0, 15.0, 0.0, 12.0, 17.0, 5.0, 2.0, 3.0], :float32)
124
+ m[0,0].should == 14.0
125
+ m[0,1].should == 9.0
126
+ m[0,2].should == 3.0
127
+ m[1,0].should == 2.0
128
+ m[1,1].should == 11.0
129
+ m[1,2].should == 15.0
130
+ m[2,0].should == 0.0
131
+ m[2,1].should == 12.0
132
+ m[2,2].should == 17.0
133
+ m[3,0].should == 5.0
134
+ m[3,1].should == 2.0
135
+ m[3,2].should == 3.0
136
+ end
137
+
138
+ [:float32, :float64].each do |dtype|
139
+ it "correctly exposes cblas_xgemm" do
140
+ #STDERR.puts "dtype=#{dtype.to_s}"
141
+ #STDERR.puts "1"
142
+ n = NMatrix.new([4,3], dtype)
143
+ n[0,0] = 14.0
144
+ n[0,1] = 9.0
145
+ n[0,2] = 3.0
146
+ n[1,0] = 2.0
147
+ n[1,1] = 11.0
148
+ n[1,2] = 15.0
149
+ n[2,0] = 0.0
150
+ n[2,1] = 12.0
151
+ n[2,2] = 17.0
152
+ n[3,0] = 5.0
153
+ n[3,1] = 2.0
154
+ n[3,2] = 3.0
155
+
156
+ m = NMatrix.new([3,2], dtype)
157
+
158
+ m[0,0] = 12.0
159
+ m[0,1] = 25.0
160
+ m[1,0] = 9.0
161
+ m[1,1] = 10.0
162
+ m[2,0] = 8.0
163
+ m[2,1] = 5.0
164
+
165
+ #c = NMatrix.new([4,2], dtype)
166
+ r = NMatrix.cblas_gemm(n, m) #, c)
167
+ #c.should equal(r) # check that both are same memory address
168
+
169
+ r[0,0].should == 273.0
170
+ r[0,1].should == 455.0
171
+ r[1,0].should == 243.0
172
+ r[1,1].should == 235.0
173
+ r[2,0].should == 244.0
174
+ r[2,1].should == 205.0
175
+ r[3,0].should == 102.0
176
+ r[3,1].should == 160.0
177
+ end
178
+ end
179
+
180
+ it "list correctly handles missing initialization value" do
181
+ NMatrix.new(:list, 3, :int8)[0,0].should == 0
182
+ NMatrix.new(:list, 4, :float64)[0,0].should == 0.0
183
+ end
184
+
185
+
186
+ ##TODO: Make this test better. It's not nearly exhaustive enough as is.
187
+ it "list correctly handles recursive removal" do
188
+ n = NMatrix.new(:list, [3,3,3], 0)
189
+ n[0,0,0] = 2
190
+ n[1,1,1] = 1
191
+ n[1,0,0] = 3
192
+ n[0,0,1] = 4
193
+
194
+ n[0,0,0].should == 2
195
+ n[1,1,1].should == 1
196
+ n[1,0,0].should == 3
197
+ n[0,0,1].should == 4
198
+
199
+ n[1,1,1] = 0
200
+ n[0,0,0].should == 2
201
+ n[1,1,1].should == 0
202
+ n[1,0,0].should == 3
203
+ n[0,0,1].should == 4
204
+ end
205
+
206
+
207
+ it "dense correctly handles missing initialization value" do
208
+ n = NMatrix.new(3, :int8)
209
+ n.stype.should == :dense
210
+ n.dtype.should == :int8
211
+
212
+ m = NMatrix.new(4, :float64)
213
+ m.stype.should == :dense
214
+ m.dtype.should == :float64
215
+ end
216
+
217
+ it "dense correctly pretty_prints complex values" do
218
+ n = NMatrix.new([4,3], COMPLEX_MATRIX43A_ARRAY, :complex128)
219
+ n.pretty_print
220
+ end
221
+
222
+ it "dense correctly handles elementwise addition" do
223
+ n = NMatrix.new(:dense, [2,2], [1,2,3,4], :int64)
224
+ m = NMatrix.new(:dense, [2,2], [-4,-1,0,66], :int64)
225
+ rcorrect = NMatrix.new(:dense, [2,2], [-3, 1, 3, 70], :int64)
226
+ r = n+m
227
+ r.should.eql? rcorrect
228
+ end
229
+
230
+ # TODO: Get it working with ROBJ too
231
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64,:rational64,:rational128,:object].each do |left_dtype|
232
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64,:rational64,:rational128,:object].each do |right_dtype|
233
+
234
+ # Won't work if they're both 1-byte, due to overflow.
235
+ next if [:byte,:int8].include?(left_dtype) && [:byte,:int8].include?(right_dtype)
236
+
237
+ # For now, don't bother testing int-int mult.
238
+ #next if [:int8,:int16,:int32,:int64].include?(left_dtype) && [:int8,:int16,:int32,:int64].include?(right_dtype)
239
+ it "dense correctly handles #{left_dtype.to_s} dot #{right_dtype.to_s} matrix multiplication" do
240
+ #STDERR.puts "dtype=#{dtype.to_s}"
241
+ #STDERR.puts "2"
242
+
243
+ nary = if left_dtype.to_s =~ /complex/
244
+ COMPLEX_MATRIX43A_ARRAY
245
+ elsif left_dtype.to_s =~ /rational/
246
+ RATIONAL_MATRIX43A_ARRAY
247
+ else
248
+ MATRIX43A_ARRAY
249
+ end
250
+
251
+ mary = if right_dtype.to_s =~ /complex/
252
+ COMPLEX_MATRIX32A_ARRAY
253
+ elsif right_dtype.to_s =~ /rational/
254
+ RATIONAL_MATRIX32A_ARRAY
255
+ else
256
+ MATRIX32A_ARRAY
257
+ end
258
+
259
+ n = NMatrix.new([4,3], nary, left_dtype)
260
+ m = NMatrix.new([3,2], mary, right_dtype)
261
+
262
+ m.shape[0].should == 3
263
+ m.shape[1].should == 2
264
+ m.rank.should == 2
265
+
266
+ n.shape[0].should == 4
267
+ n.shape[1].should == 3
268
+ n.rank.should == 2
269
+
270
+ n.shape[1].should == m.shape[0]
271
+
272
+ r = n.dot m
273
+
274
+ r[0,0].should == 273.0
275
+ r[0,1].should == 455.0
276
+ r[1,0].should == 243.0
277
+ r[1,1].should == 235.0
278
+ r[2,0].should == 244.0
279
+ r[2,1].should == 205.0
280
+ r[3,0].should == 102.0
281
+ r[3,1].should == 160.0
282
+
283
+ #r.dtype.should == :float64 unless left_dtype == :float32 && right_dtype == :float32
284
+ end
285
+ end
286
+ end
287
+
288
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64,:rational64,:rational128].each do |left_dtype|
289
+ [:byte,:int8,:int16,:int32,:int64,:float32,:float64,:rational64,:rational128].each do |right_dtype|
290
+
291
+ # Won't work if they're both 1-byte, due to overflow.
292
+ next if [:byte,:int8].include?(left_dtype) && [:byte,:int8].include?(right_dtype)
293
+
294
+ it "dense correctly handles #{left_dtype.to_s} dot #{right_dtype.to_s} vector multiplication" do
295
+ #STDERR.puts "dtype=#{dtype.to_s}"
296
+ #STDERR.puts "2"
297
+ n = 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], left_dtype)
298
+
299
+ m = NVector.new(3, [2.0, 1.0, 0.0], right_dtype)
300
+
301
+ m.shape[0].should == 3
302
+ m.shape[1].should == 1
303
+
304
+ n.shape[0].should == 4
305
+ n.shape[1].should == 3
306
+ n.rank.should == 2
307
+
308
+ n.shape[1].should == m.shape[0]
309
+
310
+ r = n.dot m
311
+ # r.class.should == NVector
312
+
313
+ r[0,0].should == 4
314
+ r[1,0].should == 13
315
+ r[2,0].should == 22
316
+ r[3,0].should == 31
317
+
318
+ #r.dtype.should == :float64 unless left_dtype == :float32 && right_dtype == :float32
319
+ end
320
+ end
321
+ end
322
+
323
+
324
+ [:dense, :list, :yale].each do |storage_type|
325
+ context "(storage: #{storage_type})" do
326
+ it "can be duplicated" do
327
+ n = NMatrix.new(storage_type, [2,3], storage_type == :yale ? :float64 : 1.1)
328
+ n.stype.should equal(storage_type)
329
+
330
+ n[0,0] = 0.0
331
+ n[0,1] = 0.1
332
+ n[1,0] = 1.0
333
+
334
+ m = n.dup
335
+ m.shape.should == n.shape
336
+ m.rank.should == n.rank
337
+ m.object_id.should_not == n.object_id
338
+ m.stype.should equal(storage_type)
339
+ m[0,0].should == n[0,0]
340
+ m[0,0] = 3.0
341
+ m[0,0].should_not == n[0,0]
342
+ end
343
+
344
+ it "enforces shape boundaries" do
345
+ lambda { NMatrix.new(storage_type, [1,10], storage_type == :yale ? :int8 : 0)[-1,0] }.should raise_error
346
+ lambda { NMatrix.new(storage_type, [1,10], storage_type == :yale ? :int8 : 0)[1,0] }.should raise_error(ArgumentError, "out of range")
347
+ lambda { NMatrix.new(storage_type, [1,10], storage_type == :yale ? :int8 : 0)[0,10] }.should raise_error(ArgumentError, "out of range")
348
+ end
349
+
350
+ it "correctly sets and gets" do
351
+ n = NMatrix.new(storage_type, 2, storage_type == :yale ? :int8 : 0)
352
+ (n[0,1] = 1).should == 1
353
+ n[0,0].should == 0
354
+ n[1,0].should == 0
355
+ n[0,1].should == 1
356
+ n[1,1].should == 0
357
+ end
358
+ end
359
+
360
+ # dense and list, not yale
361
+ context "(storage: #{storage_type})" do
362
+ it "correctly gets default value" do
363
+ NMatrix.new(storage_type, 3, 0)[1,1].should == 0
364
+ NMatrix.new(storage_type, 3, 0.1)[1,1].should == 0.1
365
+ NMatrix.new(storage_type, 3, 1)[1,1].should == 1
366
+ end
367
+
368
+ it "returns correct shape and rank" do
369
+ NMatrix.new(storage_type, [3,2,8], 0).shape.should == [3,2,8]
370
+ NMatrix.new(storage_type, [3,2,8], 0).rank.should == 3
371
+ end
372
+ end unless storage_type == :yale
373
+ end
374
+
375
+
376
+ it "correctly handles dense construction" do
377
+ NMatrix.new(3,0)[1,1].should == 0
378
+ lambda { NMatrix.new(3,:int8)[1,1] }.should_not raise_error
379
+ end
380
+
381
+ it "correctly allows iteration of Ruby object matrices" do
382
+ n = NMatrix.new(:dense, [3,3], [1,2,3,4,5,6,7,8,9], :object)
383
+ n.each do |x|
384
+ puts x
385
+ end
386
+ end
387
+
388
+ it "correctly allows iteration of non-Ruby object matrices" do
389
+ n = NMatrix.new(:dense, [3,3], [1,2,3,4,5,6,7,8,9], :int64)
390
+ n.each do |x|
391
+ puts x
392
+ end
393
+ end
394
+
395
+ end