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,54 @@
1
+
2
+ // Symbolic matrix multiply c=a*b
3
+ void %%TYPE_ABBREV%%_symbmm_(y_size_t n, y_size_t m, YALE_PARAM A, YALE_PARAM B, YALE_PARAM C)
4
+ {
5
+ u_%%TYPE%% mask[m];
6
+ u_%%TYPE%% i, j, k, kk, jj, minmn, ndnz = n; /* Local variables */
7
+
8
+ u_%%TYPE%% *ia = (u_%%TYPE%%*)(A.ia),
9
+ *ja = (u_%%TYPE%%*)(A.ja),
10
+ *ib = (u_%%TYPE%%*)(B.ia),
11
+ *jb = (u_%%TYPE%%*)(B.ja),
12
+ *ic = (u_%%TYPE%%*)(C.ia);
13
+
14
+ for (j = 0; j < m; ++j)
15
+ mask[j] = U%%TYPE_MAX%%;
16
+
17
+ if (C.diag) ic[0] = n+1;
18
+ else ic[0] = 0;
19
+
20
+ minmn = SMMP_MIN(m,n);
21
+
22
+ for (i = 0; i < n; ++i) { // MAIN LOOP: through rows
23
+
24
+ for (jj = ia[i]; jj <= ia[i+1]; ++jj) { // merge row lists, walking through columns in each row
25
+
26
+ // j <- column index given by JA[jj], or handle diagonal.
27
+ if (jj == ia[i+1]) { // Don't really do it the last time -- just handle diagonals in a new yale matrix.
28
+ if (!A.diag || i >= minmn) continue;
29
+ j = i;
30
+ } else j = ja[jj];
31
+
32
+ for (kk = ib[j]; kk <= ib[j+1]; ++kk) { // Now walk through columns of row J in matrix B.
33
+ if (kk == ib[j+1]) {
34
+ if (!B.diag || j >= minmn) continue;
35
+ k = j;
36
+ } else k = jb[kk];
37
+
38
+ if (mask[k] != i) {
39
+ mask[k] = i;
40
+ ++ndnz;
41
+ }
42
+ }
43
+ }
44
+
45
+ if (C.diag && !mask[i]) --ndnz;
46
+
47
+ ic[i+1] = ndnz;
48
+ }
49
+ } /* symbmm_ */
50
+
51
+
52
+
53
+
54
+
@@ -0,0 +1,68 @@
1
+
2
+ void %%INT_ABBREV%%_%%TYPE_ABBREV%%_transp_(y_size_t n, y_size_t m, YALE_PARAM A, YALE_PARAM B, bool move)
3
+ {
4
+ u_%%INT%% i, j, index;
5
+
6
+ u_%%INT%% *ia = (u_%%INT%%*)(A.ia),
7
+ *ja = (u_%%INT%%*)(A.ja),
8
+ *ib = (u_%%INT%%*)(B.ia),
9
+ *jb = (u_%%INT%%*)(B.ja);
10
+ %%TYPE%% *a = A.a,
11
+ *b = B.a;
12
+
13
+ // Clear B
14
+ for (i = 0; i < m+1; ++i)
15
+ ib[i] = 0;
16
+ if (move) {
17
+ for (i = 0; i < m+1; ++i) {
18
+ %%TYPE b[i] = 0%%
19
+ }
20
+ }
21
+
22
+ if (A.diag) ib[0] = m + 1;
23
+ else ib[0] = 0;
24
+
25
+ /* count indices for each column */
26
+
27
+ for (i = 0; i < n; ++i) {
28
+ for (j = ia[i]; j < ia[i+1]; ++j)
29
+ ++(ib[ja[j]+1]);
30
+ }
31
+
32
+ for (i = 0; i < m; ++i)
33
+ ib[i+1] = ib[i] + ib[i+1];
34
+
35
+ /* now make jb */
36
+
37
+ for (i = 0; i < n; ++i) {
38
+
39
+ for (j = ia[i]; j < ia[i+1]; ++j) {
40
+ index = ja[j];
41
+ jb[ib[index]] = i;
42
+
43
+ if (move)
44
+ %%TYPE b[ib[index]] = a[j]%%
45
+
46
+ ++(ib[index]);
47
+ }
48
+ }
49
+
50
+ /* now fixup ib */
51
+
52
+ for (i = m; i >= 1; --i)
53
+ ib[i] = ib[i-1];
54
+
55
+
56
+ if (A.diag) {
57
+ if (move) {
58
+ j = SMMP_MIN(n,m);
59
+
60
+ for (i = 0; i < j; ++i)
61
+ %%TYPE b[i] = a[i]%%
62
+ }
63
+ ib[0] = m + 1;
64
+
65
+ } else {
66
+ ib[0] = 0;
67
+ }
68
+ }
@@ -0,0 +1,67 @@
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
+ # == array.rb
24
+ #
25
+ # Ruby core extensions for NMatrix.
26
+
27
+ class Array
28
+ # Convert a Ruby Array to an NMatrix.
29
+ #
30
+ # You must provide a shape for the matrix as the first argument.
31
+ #
32
+ # == Arguments:
33
+ # <tt>shape</tt> :: Array describing matrix dimensions (or Fixnum for square) -- REQUIRED!
34
+ # <tt>dtype</tt> :: Override data type (e.g., to store a Float as :float32 instead of :float64) -- optional.
35
+ # <tt>stype</tt> :: Optional storage type (defaults to :dense)
36
+ def to_nm *args
37
+ pos = 0
38
+
39
+ shape = args[pos]; pos += 1
40
+
41
+ dtype = begin
42
+ if pos >= args.size
43
+ # TODO: Upcasting.
44
+ if self[0].is_a?(Fixnum)
45
+ :int64
46
+ elsif self[0].is_a?(Float)
47
+ :float64
48
+ elsif self[0].is_a?(Rational)
49
+ :rational128
50
+ elsif self[0].is_a?(Complex)
51
+ :complex128
52
+ end.tap { pos += 1 }
53
+ else
54
+ args[pos].tap { pos += 1 }
55
+ end
56
+ end
57
+
58
+ stype = args[pos].is_a?(Symbol) ? args[pos].tap { pos += 1} : :dense
59
+
60
+
61
+ if stype == :dense
62
+ NMatrix.new(stype, shape, self, dtype)
63
+ else
64
+ NMatrix.new(:dense, shape, self, dtype).cast(:stype, dtype)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,263 @@
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.rb
24
+ #
25
+ # This file loads the C extension for NMatrix, and adds a few
26
+ # additional pieces of functionality (e.g., inspect, pretty_print).
27
+ # Also provided is NVector, which represents a rank-1 NMatrix in
28
+ # vector operations.
29
+
30
+ # For some reason nmatrix.so ends up in a different place during gem build
31
+ if File.exist? "lib/nmatrix/nmatrix.so"
32
+ require File.join(File.dirname(__FILE__), "nmatrix/nmatrix.so") # development
33
+ else
34
+ require File.join(File.dirname(__FILE__), "nmatrix.so") # gem
35
+ end
36
+ require File.join(File.dirname(__FILE__), "array.rb") # Load Array extensions
37
+
38
+
39
+ class NMatrix
40
+ VERSION = '0.0.1'
41
+
42
+ #def inspect
43
+ #
44
+ #end
45
+
46
+ # TODO: Make this actually pretty.
47
+ def pretty_print
48
+ raise(NotImplementedError, "can only print rank 2 matrices") unless rank == 2
49
+ (0...shape[0]).each do |i|
50
+ arr = []
51
+ (0...shape[1]).each do |j|
52
+ arr << (self[i,j].nil? ? "nil" : self[i,j])
53
+ end
54
+ puts arr.join(" ")
55
+ end
56
+ nil
57
+ end
58
+
59
+
60
+ def inspect
61
+ original_inspect = super
62
+ original_inspect = original_inspect[0...original_inspect.size-1]
63
+ original_inspect + inspect_helper.join(" ") + ">"
64
+ end
65
+
66
+ def __yale_ary__to_s(sym)
67
+ ary = self.send("__yale_#{sym.to_s}__".to_sym)
68
+ "[" + ary.collect { |a| a.nil? ? "nil" : a }.join(',') + "]"
69
+ end
70
+
71
+ class << self
72
+
73
+ # Helper function for loading a file in the first sparse format given here:
74
+ # http://math.nist.gov/MatrixMarket/formats.html
75
+ #
76
+ # Override type specifier (e.g., 'real') using :read_with => :to_f (or any other string-to-numeric conversion
77
+ # function), and with :dtype => :float32 or :dtype => :int8 to force storage in a lesser type.
78
+ def load_matrix_matrix_coordinate_file filename, options = {}
79
+ f = File.new(filename, "r")
80
+
81
+ func = options.has_key?(:read_with) ? options[:read_with] : nil
82
+ dtype = options.has_key?(:dtype) ? options[:dtype] : nil
83
+
84
+ line = f.gets
85
+ raise(IOError, "incorrect file type specifier") unless line =~ /^%%MatrixMarket\ matrix\ coordinate/
86
+ spec = line.split
87
+ case spec[3]
88
+ when 'real'
89
+ func ||= :to_f
90
+ dtype ||= :float64
91
+ when 'integer'
92
+ func ||= :to_i
93
+ dtype ||= :int64
94
+ when 'complex'
95
+ func ||= :to_complex
96
+ dtype ||= :complex128
97
+ when 'rational'
98
+ func = :to_rational
99
+ dtype ||= :rational128
100
+ else
101
+ raise ArgumentError, "Unrecognized dtype"
102
+ end unless !func.nil? && !dtype.nil?
103
+
104
+ line = f.gets
105
+ while line =~ /^%/
106
+ line = f.gets
107
+ end
108
+
109
+ rows, cols, entries = line.split.collect { |x| x.to_i }
110
+
111
+ matrix = NMatrix.new(:yale, [rows, cols], entries, dtype)
112
+
113
+ entries.times do
114
+ i,j,v = line.split
115
+ matrix[i.to_i-1,j.to_i-1] = v.send(func)
116
+ end
117
+
118
+ matrix
119
+ end
120
+
121
+
122
+ def cblas_gemm a, b, c=nil, alpha=1.0, beta=0.0, transpose_a=false, transpose_b=false, m=nil, n=nil, k=nil, lda=nil, ldb=nil, ldc=nil
123
+ raise(ArgumentError, "expected dense NMatrices as first two arguments") unless a.is_a?(NMatrix) && b.is_a?(NMatrix) && a.stype == :dense && b.stype == :dense
124
+ raise(ArgumentError, "expected nil or dense NMatrix as third argument") unless c.nil? || (c.is_a?(NMatrix) && c.stype == :dense)
125
+ raise(ArgumentError, "NMatrix dtype mismatch") unless a.dtype == b.dtype && (c.nil? ? true : a.dtype == c.dtype)
126
+
127
+ # First, set m, n, and k, which depend on whether we're taking the transpose of a and b.
128
+ if c.nil?
129
+ if transpose_a # either :transpose or :complex_conjugate
130
+ m ||= a.shape[1]
131
+ k ||= a.shape[0]
132
+ else # no transpose
133
+ m ||= a.shape[0]
134
+ k ||= a.shape[1]
135
+ end
136
+ n ||= transpose_b ? b.shape[0] : b.shape[1]
137
+ c = NMatrix.new([m, n], a.dtype)
138
+ else
139
+ m ||= c.shape[0]
140
+ n ||= c.shape[1]
141
+ k ||= transpose_a ? a.shape[0] : a.shape[1]
142
+ end
143
+
144
+ # I think these are independent of whether or not a transpose occurs.
145
+ lda ||= a.shape[1]
146
+ ldb ||= b.shape[1]
147
+ ldc ||= c.shape[1]
148
+
149
+ if a.dtype == :complex64 || a.dtype == :complex128 # NM_COMPLEX64 and NM_COMPLEX128 both require complex alpha and beta
150
+ alpha = Complex.new(1.0, 0.0) if alpha == 1.0
151
+ beta = Complex.new(0.0, 0.0) if beta == 0.0
152
+ end
153
+
154
+ # For argument descriptions, see: http://www.netlib.org/blas/dgemm.f
155
+ NMatrix.__cblas_gemm__(transpose_a, transpose_b, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc)
156
+
157
+ return c
158
+ end
159
+
160
+
161
+ def cblas_gemv a, x, y=nil, alpha=1.0, beta=0.0, transpose_a=false, m=nil, n=nil, lda=nil, incx=nil, incy=nil
162
+ m ||= transpose_a ? a.shape[1] : a.shape[0]
163
+ n ||= transpose_a ? a.shape[0] : a.shape[1]
164
+
165
+ lda ||= a.shape[1]
166
+ incx ||= 1
167
+ incy ||= 1
168
+
169
+ if a.dtype == :complex64 || a.dtype == :complex128 # NM_COMPLEX64 and NM_COMPLEX128 both require complex alpha and beta
170
+ alpha = Complex.new(1.0, 0.0) if alpha == 1.0
171
+ beta = Complex.new(0.0, 0.0) if beta == 0.0
172
+ end
173
+
174
+ NMatrix.__cblas_gemv__(transpose_a, m, n, alpha, a, lda, x, incx, beta, y, incy)
175
+
176
+ return y
177
+ end
178
+ end
179
+
180
+ protected
181
+ def inspect_helper
182
+ ary = []
183
+ ary << "shape:[#{shape.join(',')}]" << "dtype:#{dtype}" << "stype:#{stype}"
184
+
185
+ if stype == :yale
186
+ ary << "capacity:#{capacity}" << "ija:#{__yale_ary__to_s(:ija)}" << "ia:#{__yale_ary__to_s(:ia)}" <<
187
+ "ja:#{__yale_ary__to_s(:ja)}" << "a:#{__yale_ary__to_s(:a)}" << "d:#{__yale_ary__to_s(:d)}" <<
188
+ "lu:#{__yale_ary__to_s(:lu)}" << "yale_size:#{__yale_size__}"
189
+ end
190
+
191
+ ary
192
+ end
193
+
194
+ end
195
+
196
+
197
+
198
+ # This is a specific type of NMatrix in which only one dimension is not 1. Although it is stored as a rank-2, n x 1,
199
+ # matrix, it acts as a rank-1 vector of size n. If the @orientation flag is set to :row, it is stored as 1 x n instead
200
+ # of n x 1.
201
+ class NVector < NMatrix
202
+ def initialize length, *args
203
+ super :dense, [length,1], *args
204
+ end
205
+
206
+ # Orientation defaults to column (e.g., [3,1] is a column of length 3). It may also be row, e.g., for [1,5].
207
+ def orientation
208
+ defined?(@orientation) && !@orientation.nil? ? @orientation : :column
209
+ end
210
+
211
+ def transpose
212
+ t = super
213
+ t.send :eval, "@orientation = @orientation == :row ? :column : :row"
214
+ t
215
+ end
216
+
217
+ def transpose!
218
+ super
219
+ @orientation = @orientation == :row ? :column : :row
220
+ self
221
+ end
222
+
223
+ def multiply m
224
+ v = super(m)
225
+ v.send :eval, "@orientation = @orientation == :row ? :column : :row"
226
+ v
227
+ end
228
+
229
+ def multiply! m
230
+ super
231
+ @orientation = @orientation == :row ? :column : :row
232
+ self
233
+ end
234
+
235
+ def [] i
236
+ @orientation == :row ? super(0,i) : super(i,0)
237
+ end
238
+
239
+ def []= i,val
240
+ @orientation == :row ? super(0,i,val) : super(i,0,val)
241
+ end
242
+
243
+ def rank; 1; end
244
+
245
+ # TODO: Make this actually pretty.
246
+ def pretty_print
247
+ dim = @orientation == :row ? 1 : 0
248
+ arr = []
249
+ (0...shape[dim]).each do |i|
250
+ arr << self[i]
251
+ end
252
+ puts arr.join(" ")
253
+ nil
254
+ end
255
+
256
+ protected
257
+ def inspect_helper
258
+ ary = super
259
+ ary << "orientation:#{(@orientation || 'column').to_s}"
260
+ ary
261
+ end
262
+
263
+ end