nmatrix 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -26,6 +26,6 @@ class NMatrix
26
26
  # Note that the format of the VERSION string is needed for NMatrix
27
27
  # native IO. If you change the format, please make sure that native
28
28
  # IO can still understand NMatrix::VERSION.
29
- VERSION = "0.0.4"
29
+ VERSION = "0.0.5"
30
30
  end
31
31
 
@@ -0,0 +1,112 @@
1
+ #--
2
+ # = NMatrix
3
+ #
4
+ # A linear algebra library for scientific computation in Ruby.
5
+ # NMatrix is part of SciRuby.
6
+ #
7
+ # NMatrix was originally inspired by and derived from NArray, by
8
+ # Masahiro Tanaka: http://narray.rubyforge.org
9
+ #
10
+ # == Copyright Information
11
+ #
12
+ # SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
13
+ # NMatrix is Copyright (c) 2013, Ruby Science Foundation
14
+ #
15
+ # Please see LICENSE.txt for additional copyright notices.
16
+ #
17
+ # == Contributing
18
+ #
19
+ # By contributing source code to SciRuby, you agree to be bound by
20
+ # our Contributor Agreement:
21
+ #
22
+ # * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
23
+ #
24
+ # == yale_functions.rb
25
+ #
26
+ # This file contains some shortcut functions for the specialty
27
+ # Yale matrix extensions (mostly for debugging and experimental
28
+ # purposes, but sometimes applicable when you need to speed up
29
+ # your code a lot).
30
+ #++
31
+
32
+ module NMatrix::YaleFunctions
33
+ # call-seq:
34
+ # yale_nd_row_size(i) -> Fixnum
35
+ #
36
+ # Returns the size of a given non-diagonal row.
37
+ def yale_nd_row_size i
38
+ yale_ija(i+1) - yale_ija(i)
39
+ end
40
+
41
+ # call-seq:
42
+ # yale_nd_row_as_array -> Array
43
+ #
44
+ # Returns the non-diagonal column indices which are stored in a given row.
45
+ def yale_nd_row_as_array i
46
+ yale_nd_row(i, :array)
47
+ end
48
+
49
+ # call-seq:
50
+ # yale_nd_row_as_set -> Set
51
+ #
52
+ # Returns the non-diagonal column indices which are stored in a given row, as a Set.
53
+ def yale_nd_row_as_set i
54
+ require 'set'
55
+ yale_nd_row(i, :array).to_set
56
+ end
57
+
58
+ # call-seq:
59
+ # yale_nd_row_as_sorted_set -> SortedSet
60
+ #
61
+ # Returns the non-diagonal column indices which are stored in a given row, as a Set.
62
+ def yale_nd_row_as_sorted_set i
63
+ require 'set'
64
+ SortedSet.new(yale_nd_row(i, :array))
65
+ end
66
+
67
+ # call-seq:
68
+ # yale_nd_row_as_hash -> Hash
69
+ #
70
+ # Returns the non-diagonal column indices and entries stored in a given row.
71
+ def yale_nd_row_as_hash i
72
+ yale_nd_row(i, :hash)
73
+ end
74
+
75
+ # call-seq:
76
+ # yale_row_as_array -> Array
77
+ #
78
+ # Returns the diagonal and non-digonal column indices stored in a given row.
79
+ def yale_row_as_array i
80
+ ary = yale_nd_row(i, :array)
81
+ return ary if i >= self.shape[1] || self[i,i].nil? || self[i,i] == 0
82
+ ary << i
83
+ end
84
+
85
+ # call-seq:
86
+ # yale_row_as_set -> Set
87
+ #
88
+ # Returns the diagonal and non-diagonal column indices stored in a given row.
89
+ def yale_row_as_set i
90
+ require 'set'
91
+ yale_row_as_array(i).to_set
92
+ end
93
+
94
+ # call-seq:
95
+ # yale_row_as_sorted_set -> SortedSet
96
+ #
97
+ # Returns the diagonal and non-diagonal column indices stored in a given row.
98
+ def yale_row_as_sorted_set i
99
+ require 'set'
100
+ SortedSet.new(yale_row_as_array(i))
101
+ end
102
+
103
+ # call-seq:
104
+ # yale_row_as_hash -> Hash
105
+ #
106
+ # Returns the diagonal and non-diagonal column indices and entries stored in a given row.
107
+ def yale_row_as_hash i
108
+ h = yale_nd_row(i, :hash)
109
+ return h if i >= self.shape[1] || self[i,i].nil? || self[i,i] == 0
110
+ h[i] = self[i,i]
111
+ end
112
+ end
data/spec/blas_spec.rb CHANGED
@@ -170,6 +170,17 @@ describe NMatrix::BLAS do
170
170
  NMatrix::BLAS.gemv(a, x)
171
171
  end
172
172
 
173
+ it "exposes asum" do
174
+ x = NVector.new(4, [1,2,3,4], :float64)
175
+ NMatrix::BLAS.asum(x).should == 10.0
176
+ end
177
+
178
+
179
+ it "exposes nrm2" do
180
+ x = NVector.new(4, [2,-4,3,5], :float64)
181
+ NMatrix::BLAS.nrm2(x, 1, 3).should be_within(1e-10).of(5.385164807134504)
182
+ end
183
+
173
184
  end
174
185
  end
175
186
  end
@@ -74,7 +74,9 @@ describe NMatrix do
74
74
  (@n/m).should == r
75
75
  end
76
76
 
77
- it "should perform element-wise modulo"
77
+ it "should perform element-wise modulo" do
78
+ pending "% operator not yet implemented for matrices"
79
+ end
78
80
 
79
81
  it "should handle element-wise equality (=~)" do
80
82
  (@n =~ @m).cast(:dense, :byte).should == NMatrix.new(:dense, 2, [0, 1, 1, 0], :byte)
@@ -131,6 +133,7 @@ describe NMatrix do
131
133
  end
132
134
 
133
135
  it "modulo" do
136
+ pending "% operator not yet implemented"
134
137
  r = @n % @m
135
138
  r.should == NMatrix.new(:dense, [2,2], [0, 1, 2, 3], :int64)
136
139
  end
data/spec/io_spec.rb CHANGED
@@ -70,6 +70,14 @@ describe NMatrix::IO do
70
70
  `wc -l spec/utm5940.mtx`.split[0].should == `wc -l spec/utm5940.saved.mtx`.split[0]
71
71
  end
72
72
 
73
+ it "raises an error when reading a non-existent file" do
74
+ fn = rand(10000000).to_i.to_s
75
+ while File.exist?(fn)
76
+ fn = rand(10000000).to_i.to_s
77
+ end
78
+ expect{ NMatrix.read(fn) }.to raise_error(Errno::ENOENT)
79
+ end
80
+
73
81
  it "reads and writes NMatrix dense" do
74
82
  n = NMatrix.new(:dense, [4,3], [0,1,2,3,4,5,6,7,8,9,10,11], :int32)
75
83
  n.write("test-out")
data/spec/lapack_spec.rb CHANGED
@@ -47,13 +47,26 @@ describe NMatrix::LAPACK do
47
47
  it "exposes clapack getrf" do
48
48
  a = NMatrix.new(:dense, 3, [4,9,2,3,5,7,8,1,6], dtype)
49
49
  NMatrix::LAPACK::clapack_getrf(:row, 3, 3, a, 3)
50
- a[0,0].should == 8
51
- a[0,1].should == 1
52
- a[0,2].should == 6
53
- a[1,0].should == 1.quo(2)
54
- a[1,1].should == 17.quo(2)
55
- a[1,2].should == -1
56
- a[2,0].should == 3.quo(8)
50
+
51
+ # delta varies for different dtypes
52
+ err = case dtype
53
+ when :float32, :complex64
54
+ 1e-6
55
+ when :float64, :complex128
56
+ 1e-15
57
+ else
58
+ 1e-64 # FIXME: should be 0, but be_within(0) does not work.
59
+ end
60
+
61
+ a[0,0].should == 9 # 8
62
+ a[0,1].should be_within(err).of(2.quo(9)) # 1
63
+ a[0,2].should be_within(err).of(4.quo(9)) # 6
64
+ a[1,0].should == 5 # 1.quo(2)
65
+ a[1,1].should be_within(err).of(53.quo(9)) # 17.quo(2)
66
+ a[1,2].should be_within(err).of(7.quo(53)) # -1
67
+ a[2,0].should == 1 # 3.quo(8)
68
+ a[2,1].should be_within(err).of(52.quo(9))
69
+ a[2,2].should be_within(err).of(360.quo(53))
57
70
  # FIXME: these are rounded, == won't work
58
71
  #a[2,1].should == 0.544118
59
72
  #a[2,2].should == 5.294118
@@ -61,10 +74,14 @@ describe NMatrix::LAPACK do
61
74
 
62
75
  it "exposes clapack potrf" do
63
76
  # first do upper
64
- a = NMatrix.new(:dense, 3, [25,15,-5, 0,18,0, 0,0,11], dtype)
65
- NMatrix::LAPACK::clapack_potrf(:row, :upper, 3, a, 3)
66
- b = NMatrix.new(:dense, 3, [5,3,-1, 0,3,1, 0,0,3], dtype)
67
- a.should == b
77
+ begin
78
+ a = NMatrix.new(:dense, 3, [25,15,-5, 0,18,0, 0,0,11], dtype)
79
+ NMatrix::LAPACK::clapack_potrf(:row, :upper, 3, a, 3)
80
+ b = NMatrix.new(:dense, 3, [5,3,-1, 0,3,1, 0,0,3], dtype)
81
+ a.should == b
82
+ rescue NotImplementedError => e
83
+ pending e.to_s
84
+ end
68
85
 
69
86
  # then do lower
70
87
  a = NMatrix.new(:dense, 3, [25,0,0, 15,18,0,-5,0,11], dtype)
@@ -89,13 +106,18 @@ describe NMatrix::LAPACK do
89
106
  it "exposes clapack getri" do
90
107
  a = NMatrix.new(:dense, 3, [1,0,4,1,1,6,-3,0,-10], dtype)
91
108
  ipiv = NMatrix::LAPACK::clapack_getrf(:row, 3, 3, a, 3) # get pivot from getrf, use for getri
92
- NMatrix::LAPACK::clapack_getri(:row, 3, a, 3, ipiv)
93
109
 
94
- b = NMatrix.new(:dense, 3, [-5,0,-2,-4,1,-1,1.5,0,0.5], dtype)
95
- a.should == b
110
+ begin
111
+ NMatrix::LAPACK::clapack_getri(:row, 3, a, 3, ipiv)
112
+
113
+ b = NMatrix.new(:dense, 3, [-5,0,-2,-4,1,-1,1.5,0,0.5], dtype)
114
+ a.should == b
115
+ rescue NotImplementedError => e
116
+ pending e.to_s
117
+ end
96
118
  end
97
119
 
98
120
 
99
121
  end
100
122
  end
101
- end
123
+ end
data/spec/leakcheck.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "./lib/nmatrix"
2
+
3
+ # Fixed:
4
+ #n = NMatrix.new(:yale, [8,2], :int64)
5
+ #m = NMatrix.new(:yale, [2,8], :int64)
6
+ #100.times do
7
+ # n.dot(m)
8
+ #end
9
+ #GC.start
10
+
11
+ # Remaining:
12
+ 100.times do |t|
13
+ n = NMatrix.new(:dense, 1000, :float64)
14
+ n[0,t] = 1.0
15
+ puts n[t,0]
16
+ end
data/spec/math_spec.rb CHANGED
@@ -50,7 +50,11 @@ describe "math" do
50
50
  it "should correctly invert a matrix" do
51
51
  a = NMatrix.new(:dense, 3, [1,0,4,1,1,6,-3,0,-10], dtype)
52
52
  b = NMatrix.new(:dense, 3, [-5,0,-2,-4,1,-1,1.5,0,0.5], dtype)
53
- a.invert!
53
+ begin
54
+ a.invert!
55
+ rescue NotImplementedError => e
56
+ pending e.to_s
57
+ end
54
58
  a.should == b
55
59
  end
56
60
  end
@@ -125,7 +129,7 @@ describe "math" do
125
129
  #STDERR.puts "2"
126
130
  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)
127
131
 
128
- m = NVector.new(3, [2.0, 1.0, 0.0], right_dtype)
132
+ m = NVector.new(3, [2.0, 1.0, 0.0], right_dtype).transpose
129
133
 
130
134
  m.shape[0].should == 3
131
135
  m.shape[1].should == 1
data/spec/nmatrix_spec.rb CHANGED
@@ -25,8 +25,7 @@
25
25
  # Basic tests for NMatrix.
26
26
  #
27
27
 
28
- # Can we use require_relative here instead?
29
- require File.join(File.dirname(__FILE__), "spec_helper.rb")
28
+ require File.dirname(__FILE__) + "/spec_helper.rb"
30
29
 
31
30
  describe NMatrix do
32
31
 
@@ -142,7 +141,7 @@ describe NMatrix do
142
141
 
143
142
  [:dense, :list, :yale].each do |storage_type|
144
143
  context storage_type do
145
- it "can be duplicated" do
144
+ it "can be duplicated" do
146
145
  n = NMatrix.new(storage_type, [2,3], storage_type == :yale ? :float64 : 1.1)
147
146
  n.stype.should equal(storage_type)
148
147
 
@@ -292,8 +291,215 @@ describe NMatrix do
292
291
  end
293
292
  end
294
293
 
294
+ context "dense" do
295
+ it "should return the matrix being iterated over when each is called with a block" do
296
+ a = NMatrix.new(2, 1)
297
+ val = (a.each { })
298
+ val.should eq a
299
+ end
300
+
301
+ it "should return the matrix being iterated over when each_stored_with_indices is called with a block" do
302
+ a = NMatrix.new(2,1)
303
+ val = (a.each_stored_with_indices { })
304
+ val.should eq a
305
+ end
306
+ end
307
+
308
+ [:list, :yale].each do |storage_type|
309
+ context storage_type do
310
+ it "should return the matrix being iterated over when each_stored_with_indices is called with a block" do
311
+ n = NMatrix.new(storage_type, [2,3], storage_type == :yale ? :float64 : 1.1)
312
+ val = (n.each_stored_with_indices { })
313
+ val.should eq n
314
+ end
315
+
316
+ it "should return an enumerator when each_stored_with_indices is called without a block" do
317
+ n = NMatrix.new(storage_type, [2,3], storage_type == :yale ? :float64 : 1.1)
318
+ val = n.each_stored_with_indices
319
+ val.should be_a Enumerator
320
+ end
321
+
322
+ end
323
+ end
324
+
295
325
  it "should iterate through element 256 without a segfault" do
296
326
  t = NVector.random(256)
297
327
  t.each { |x| x + 0 }
298
328
  end
329
+
330
+ context "mapping and reduction related functions" do
331
+
332
+ before :each do
333
+ @nm_1d = N[5.0,0.0,1.0,2.0,3.0]
334
+ @nm_2d = N[[0.0,1.0],[2.0,3.0]]
335
+ end
336
+
337
+ it "behaves like Enumerable#reduce with no argument to reduce" do
338
+ @nm_1d.reduce_along_dim(0) { |acc, el| acc + el }.to_f.should eq 11
339
+ @nm_2d.reduce_along_dim(1) { |acc, el| acc + el }.should eq N[[1, 5]]
340
+ end
341
+
342
+ it "should calculate the mean along the specified dimension" do
343
+ @nm_1d.mean.should eq N[2.2]
344
+ @nm_2d.mean.should eq N[[1.0,2.0]]
345
+ end
346
+
347
+ it "should calculate the minimum along the specified dimension" do
348
+ @nm_1d.min.should eq 0.0
349
+ @nm_2d.min.should eq N[[0.0, 1.0]]
350
+ @nm_2d.min(1).should eq N[[0.0], [2.0]]
351
+ end
352
+
353
+ it "should calculate the maximum along the specified dimension" do
354
+ @nm_1d.max.should eq 5.0
355
+ @nm_2d.max.should eq N[[2.0, 3.0]]
356
+ end
357
+
358
+ it "should calculate the variance along the specified dimension" do
359
+ @nm_1d.variance.should eq N[3.7]
360
+ @nm_2d.variance(1).should eq N[[0.5], [0.5]]
361
+ end
362
+
363
+ it "should calculate the sum along the specified dimension" do
364
+ @nm_1d.sum.should eq N[11]
365
+ @nm_2d.sum.should eq N[[2], [4]]
366
+ end
367
+
368
+ it "should calculate the standard deviation along the specified dimension" do
369
+ @nm_1d.std.should eq N[Math.sqrt(3.7)]
370
+ @nm_2d.std(1).should eq N[[Math.sqrt(0.5)], [Math.sqrt(0.5)]]
371
+ end
372
+
373
+ it "should raise an ArgumentError when any invalid dimension is provided" do
374
+ expect { @nm_1d.mean(3) }.to raise_exception(ArgumentError)
375
+ end
376
+
377
+ it "should convert to float if it contains only a single element" do
378
+ N[4.0].to_f.should eq 4.0
379
+ N[[[[4.0]]]].to_f.should eq 4.0
380
+ end
381
+
382
+ it "should raise an index error if it contains more than a single element" do
383
+ expect { @nm_1d.to_f }.to raise_error(IndexError)
384
+ end
385
+
386
+ it "should map a block to all elements" do
387
+ @nm_1d.map { |e| e ** 2 }.should eq N[25.0,0.0,1.0,4.0,9.0]
388
+ @nm_2d.map { |e| e ** 2 }.should eq N[[0.0,1.0],[4.0,9.0]]
389
+ end
390
+
391
+ it "should map! a block to all elements in place" do
392
+ fct = Proc.new { |e| e ** 2 }
393
+ expected1 = @nm_1d.map &fct
394
+ expected2 = @nm_2d.map &fct
395
+ @nm_1d.map! &fct
396
+ @nm_1d.should eq expected1
397
+ @nm_2d.map! &fct
398
+ @nm_2d.should eq expected2
399
+ end
400
+
401
+ it "should return an enumerator for map without a block" do
402
+ @nm_1d.map.should be_a Enumerator
403
+ end
404
+
405
+ it "should return an enumerator for reduce without a block" do
406
+ @nm_1d.reduce_along_dim(0).should be_a Enumerator
407
+ end
408
+
409
+ it "should return an enumerator for each_along_dim without a block" do
410
+ @nm_1d.each_along_dim(0).should be_a Enumerator
411
+ end
412
+
413
+ it "should iterate correctly for map without a block" do
414
+ en = @nm_1d.map
415
+ en.each { |e| e**2 }.should eq @nm_1d.map { |e| e**2 }
416
+ en = @nm_2d.map
417
+ en.each { |e| e**2 }.should eq @nm_2d.map { |e| e**2 }
418
+ end
419
+
420
+ it "should iterate correctly for reduce without a block" do
421
+ en = @nm_1d.reduce_along_dim(0, 1.0)
422
+ en.each { |a, e| a+e }.to_f.should eq 12
423
+ en = @nm_2d.reduce_along_dim(1, 1.0)
424
+ en.each { |a, e| a+e }.should eq N[[2.0],[6.0]]
425
+ end
426
+
427
+ it "should iterate correctly for each_along_dim without a block" do
428
+ res = NMatrix.zeros_like(@nm_1d[0...1])
429
+ en = @nm_1d.each_along_dim(0)
430
+ en.each { |e| res += e }
431
+ res.to_f.should eq 11
432
+
433
+ res = NMatrix.zeros_like (@nm_2d[0...2, 0])
434
+ en = @nm_2d.each_along_dim(1)
435
+ en.each { |e| res += e }
436
+ res.should eq N[[1.0], [5.0]]
437
+ end
438
+
439
+ it "should yield matrices of matching dtype for each_along_dim" do
440
+ m = NMatrix.new([2,3], [1,2,3,3,4,5], :complex128)
441
+ m.each_along_dim(1) do |sub_m|
442
+ sub_m.dtype.should eq :complex128
443
+ end
444
+ end
445
+
446
+ it "should reduce to a matrix of matching dtype for reduce_along_dim" do
447
+ m = NMatrix.new([2,3], [1,2,3,3,4,5], :complex128)
448
+ m.reduce_along_dim(1) do |acc, sub_m|
449
+ sub_m.dtype.should eq :complex128
450
+ acc
451
+ end
452
+
453
+ m = NMatrix.new([2,3], [1,2,3,3,4,5], :complex128)
454
+ m.reduce_along_dim(1, 0.0) do |acc, sub_m|
455
+ sub_m.dtype.should eq :complex128
456
+ acc
457
+ end
458
+ end
459
+
460
+ it "should allow overriding the dtype for reduce_along_dim" do
461
+ m = N[[1,2,3], [3,4,5], :complex128]
462
+ m.reduce_along_dim(1, 0.0, :float64) do |acc, sub_m|
463
+ acc.dtype.should eq :float64
464
+ acc
465
+ end
466
+
467
+ m = N[[1,2,3], [3,4,5], :complex128]
468
+ m.reduce_along_dim(1, nil, :float64) do |acc, sub_m|
469
+ acc.dtype.should eq :float64
470
+ acc
471
+ end
472
+ end
473
+
474
+ it "should convert integer dtypes to float when calculating mean" do
475
+ m = N[[1,2,3], [3,4,5], :int32]
476
+ m.mean(0).dtype.should eq :float64
477
+ end
478
+
479
+ it "should convert integer dtypes to float when calculating variance" do
480
+ m = N[[1,2,3], [3,4,5], :int32]
481
+ m.mean(0).dtype.should eq :float64
482
+ end
483
+
484
+ it "should convert integer dtypes to float when calculating standard deviation" do
485
+ m = N[[1,2,3], [3,4,5], :int32]
486
+ m.mean(0).dtype.should eq :float64
487
+ end
488
+
489
+ context "_like constructors" do
490
+
491
+ it "should create an nmatrix of ones with dimensions and type the same as its argument" do
492
+ NMatrix.ones_like(@nm_1d).should eq N[1.0, 1.0, 1.0, 1.0, 1.0]
493
+ NMatrix.ones_like(@nm_2d).should eq N[[1.0, 1.0], [1.0, 1.0]]
494
+ end
495
+
496
+ it "should create an nmatrix of zeros with dimensions and type the same as its argument" do
497
+ NMatrix.zeros_like(@nm_1d).should eq N[0.0, 0.0, 0.0, 0.0, 0.0]
498
+ NMatrix.zeros_like(@nm_2d).should eq N[[0.0, 0.0], [0.0, 0.0]]
499
+ end
500
+
501
+ end
502
+
503
+ end
504
+
299
505
  end