nmatrix 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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