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.
- data/.autotest +23 -0
- data/.gemtest +0 -0
- data/Gemfile +7 -0
- data/History.txt +6 -0
- data/LICENSE.txt +21 -0
- data/Manifest.txt +51 -0
- data/README.rdoc +63 -0
- data/Rakefile +154 -0
- data/ext/nmatrix/cblas.c +150 -0
- data/ext/nmatrix/dense.c +307 -0
- data/ext/nmatrix/dense/blas_header.template.c +52 -0
- data/ext/nmatrix/dense/elementwise.template.c +107 -0
- data/ext/nmatrix/dense/gemm.template.c +159 -0
- data/ext/nmatrix/dense/gemv.template.c +130 -0
- data/ext/nmatrix/dense/rationalmath.template.c +68 -0
- data/ext/nmatrix/depend +18 -0
- data/ext/nmatrix/extconf.rb +143 -0
- data/ext/nmatrix/generator.rb +594 -0
- data/ext/nmatrix/generator/syntax_tree.rb +481 -0
- data/ext/nmatrix/list.c +774 -0
- data/ext/nmatrix/nmatrix.c +1977 -0
- data/ext/nmatrix/nmatrix.h +912 -0
- data/ext/nmatrix/rational.c +98 -0
- data/ext/nmatrix/yale.c +726 -0
- data/ext/nmatrix/yale/complexmath.template.c +71 -0
- data/ext/nmatrix/yale/elementwise.template.c +46 -0
- data/ext/nmatrix/yale/elementwise_op.template.c +73 -0
- data/ext/nmatrix/yale/numbmm.template.c +94 -0
- data/ext/nmatrix/yale/smmp1.template.c +21 -0
- data/ext/nmatrix/yale/smmp1_header.template.c +38 -0
- data/ext/nmatrix/yale/smmp2.template.c +43 -0
- data/ext/nmatrix/yale/smmp2_header.template.c +46 -0
- data/ext/nmatrix/yale/sort_columns.template.c +56 -0
- data/ext/nmatrix/yale/symbmm.template.c +54 -0
- data/ext/nmatrix/yale/transp.template.c +68 -0
- data/lib/array.rb +67 -0
- data/lib/nmatrix.rb +263 -0
- data/lib/string.rb +65 -0
- data/spec/nmatrix_spec.rb +395 -0
- data/spec/nmatrix_yale_spec.rb +239 -0
- data/spec/nvector_spec.rb +43 -0
- data/spec/syntax_tree_spec.rb +46 -0
- metadata +150 -0
@@ -0,0 +1,594 @@
|
|
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
|
+
# == generator.rb
|
24
|
+
#
|
25
|
+
# Module for generating source files.
|
26
|
+
|
27
|
+
$RELATIVE_PATH = nil
|
28
|
+
|
29
|
+
$IN_MAKEFILE = begin
|
30
|
+
dir_pwd_split = Dir.pwd.split('/')
|
31
|
+
if dir_pwd_split.size >= 4 && dir_pwd_split[-4] == "tmp" # when running make by hand
|
32
|
+
$RELATIVE_PATH = "../../../../"
|
33
|
+
true
|
34
|
+
elsif dir_pwd_split[-2] == "ext" # when building gem
|
35
|
+
$RELATIVE_PATH = File.join(File.dirname(__FILE__), "../..")
|
36
|
+
true
|
37
|
+
else # when building in development dir
|
38
|
+
$RELATIVE_PATH = File.dirname(__FILE__)
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
require File.join($RELATIVE_PATH, "lib/string.rb") # from the Makefile
|
44
|
+
require File.join($RELATIVE_PATH, "ext/nmatrix/generator/syntax_tree.rb")
|
45
|
+
|
46
|
+
class DTypeInfo < Struct.new(:enum, :sizeof, :sym, :id, :type)
|
47
|
+
def max_macro
|
48
|
+
typename = self.sizeof.to_s
|
49
|
+
if typename.include?('_')
|
50
|
+
ary = typename.split('_')
|
51
|
+
typename = ary[0...ary.size-1].join('')
|
52
|
+
end
|
53
|
+
typename.upcase + "_MAX"
|
54
|
+
end
|
55
|
+
|
56
|
+
def min_macro
|
57
|
+
typename = self.sizeof.to_s
|
58
|
+
if typename.include?('_')
|
59
|
+
ary = typename.split('_')
|
60
|
+
typename = ary[0...ary.size-1].join('')
|
61
|
+
end
|
62
|
+
typename.upcase + "_MIN"
|
63
|
+
end
|
64
|
+
|
65
|
+
# What type would this be if we used the maximum number of bytes available?
|
66
|
+
def long_dtype
|
67
|
+
Generator::DTYPES.select { |x| x.type == self.type }.last
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
class Array
|
73
|
+
def max
|
74
|
+
found_max = nil
|
75
|
+
self.each_index do |i|
|
76
|
+
found_max = self[i] if found_max.nil? || self[i] > found_max
|
77
|
+
end
|
78
|
+
found_max
|
79
|
+
end
|
80
|
+
|
81
|
+
def min
|
82
|
+
found_min = nil
|
83
|
+
self.each_index do |i|
|
84
|
+
found_min = self[i] if found_min.nil? || self[i] < found_min
|
85
|
+
end
|
86
|
+
found_min
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
module Generator
|
92
|
+
SRC_DIR = File.join("ext", "nmatrix")
|
93
|
+
DTYPES = [
|
94
|
+
# enum sizeof sym id type
|
95
|
+
[:NM_NONE, 0, :none, 0, :none],
|
96
|
+
[:NM_BYTE, :u_int8_t, :byte, :b, :int],
|
97
|
+
[:NM_INT8, :int8_t, :int8, :i8, :int],
|
98
|
+
[:NM_INT16, :int16_t, :int16, :i16, :int],
|
99
|
+
[:NM_INT32, :int32_t, :int32, :i32, :int],
|
100
|
+
[:NM_INT64, :int64_t, :int64, :i64, :int],
|
101
|
+
[:NM_FLOAT32, :float, :float32, :f32, :float],
|
102
|
+
[:NM_FLOAT64, :double, :float64, :f64, :float],
|
103
|
+
[:NM_COMPLEX64, :complex64, :complex64, :c64, :complex],
|
104
|
+
[:NM_COMPLEX128, :complex128, :complex128, :c128, :complex],
|
105
|
+
[:NM_RATIONAL32, :rational32, :rational32, :r32, :rational],
|
106
|
+
[:NM_RATIONAL64, :rational64, :rational64, :r64, :rational],
|
107
|
+
[:NM_RATIONAL128, :rational128, :rational128, :r128, :rational],
|
108
|
+
[:NM_ROBJ, :VALUE, :object, :v, :value],
|
109
|
+
[:NM_TYPES, 0, :dtypes, 0, :none]
|
110
|
+
].map { |d| DTypeInfo.new(*d) }
|
111
|
+
|
112
|
+
INDEX_DTYPES = DTYPES.select { |dtype| dtype.type == :int && dtype.id != :b }
|
113
|
+
INTEGER_DTYPES = DTYPES.select { |dtype| dtype.type == :int }
|
114
|
+
RATIONAL_DTYPES = DTYPES.select { |dtype| dtype.type == :rational }
|
115
|
+
NONBLAS_DTYPES = DTYPES.select { |dtype| [:int,:rational,:value].include?(dtype.type) }
|
116
|
+
COMPLEX_DTYPES = DTYPES.select { |dtype| dtype.type == :complex }
|
117
|
+
FLOAT_DTYPES = DTYPES.select { |dtype| dtype.type == :float }
|
118
|
+
OBJECT_DTYPES = DTYPES.select { |dtype| dtype.type == :value }
|
119
|
+
ACTUAL_DTYPES = DTYPES.select { |dtype| dtype.type != :none }
|
120
|
+
|
121
|
+
YIELD_REGEX = /%%=\ [^%%]*%%/
|
122
|
+
|
123
|
+
|
124
|
+
DTYPES_ASSIGN = {
|
125
|
+
:complex => { # Assign a complex to:
|
126
|
+
:complex => lambda {|l,r| "((#{l}*)p1)->r = ((#{r}*)p2)->r; ((#{l}*)p1)->i = ((#{r}*)p2)->i;" },
|
127
|
+
:float => lambda {|l,r| "*(#{l}*)p1 = ((#{r}*)p2)->r;" },
|
128
|
+
:int => lambda {|l,r| "*(#{l}*)p1 = ((#{r}*)p2)->r;" },
|
129
|
+
:rational => lambda {|l,r| "rb_raise(rb_eNotImpError, \"I don't know how to assign a complex to a rational\");" },
|
130
|
+
:value => lambda {|l,r| "*(VALUE*)p1 = rb_complex_new(rb_float_new(((#{r}*)p2)->r), rb_float_new(((#{r}*)p2)->i));" },
|
131
|
+
},
|
132
|
+
:float => {
|
133
|
+
:complex => lambda {|l,r| "((#{l}*)p1)->i = 0; ((#{l}*)p1)->r = *(#{r}*)p2;" },
|
134
|
+
:float => lambda {|l,r| "*(#{l}*)p1 = *(#{r}*)p2;" },
|
135
|
+
:int => lambda {|l,r| "*(#{l}*)p1 = *(#{r}*)p2;" },
|
136
|
+
:rational => lambda {|l,r| "rb_raise(rb_eNotImpError, \"I don't know how to assign a float to a rational\");" },
|
137
|
+
:value => lambda {|l,r| "*(VALUE*)p1 = rb_float_new(*(#{r}*)p2);" },
|
138
|
+
},
|
139
|
+
:int => {
|
140
|
+
:complex => lambda {|l,r| "((#{l}*)p1)->i = 0; ((#{l}*)p1)->r = *(#{r}*)p2;" },
|
141
|
+
:float => lambda {|l,r| "*(#{l}*)p1 = *(#{r}*)p2;" },
|
142
|
+
:int => lambda {|l,r| "*(#{l}*)p1 = *(#{r}*)p2;" },
|
143
|
+
:rational => lambda {|l,r| "((#{l}*)p1)->d = 1; ((#{l}*)p1)->n = *(#{r}*)p2;" },
|
144
|
+
:value => lambda {|l,r| "*(VALUE*)p1 = INT2NUM(*(#{r}*)p2);" },
|
145
|
+
},
|
146
|
+
:rational => {
|
147
|
+
:complex => lambda {|l,r| "((#{l}*)p1)->i = 0; ((#{l}*)p1)->r = ((#{r}*)p2)->n / (double)((#{r}*)p2)->d;" },
|
148
|
+
:float => lambda {|l,r| "*(#{l}*)p1 = ((#{r}*)p2)->n / (double)((#{r}*)p2)->d;" },
|
149
|
+
:int => lambda {|l,r| "*(#{l}*)p1 = ((#{r}*)p2)->n / ((#{r}*)p2)->d;" },
|
150
|
+
:rational => lambda {|l,r| "((#{l}*)p1)->d = ((#{r}*)p2)->d; ((#{l}*)p1)->n = ((#{r}*)p2)->n;" },
|
151
|
+
:value => lambda {|l,r| "*(VALUE*)p1 = rb_rational_new(INT2FIX(((#{r}*)p2)->n), INT2FIX(((#{r}*)p2)->d));" }
|
152
|
+
},
|
153
|
+
:value => {
|
154
|
+
:complex => lambda {|l,r| "((#{l}*)p1)->r = REAL2DBL(*(VALUE*)p2); ((#{l}*)p1)->i = IMAG2DBL(*(VALUE*)p2);" },
|
155
|
+
:float => lambda {|l,r| "*(#{l}*)p1 = NUM2DBL(*(VALUE*)p2);"},
|
156
|
+
:int => lambda {|l,r| "*(#{l}*)p1 = NUM2DBL(*(VALUE*)p2);"},
|
157
|
+
:rational => lambda {|l,r| "((#{l}*)p1)->n = NUMER2INT(*(VALUE*)p2); ((#{l}*)p1)->d = DENOM2INT(*(VALUE*)p2);" },
|
158
|
+
:value => lambda {|l,r| "*(VALUE*)p1 = *(VALUE*)p2;"}
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
|
163
|
+
class << self
|
164
|
+
|
165
|
+
def decl spec_name, ary
|
166
|
+
a = []
|
167
|
+
a << "#{spec_name} {"
|
168
|
+
ary.each do |v|
|
169
|
+
a << " #{v.to_s},"
|
170
|
+
end
|
171
|
+
a << "};"
|
172
|
+
a.join("\n") + "\n\n"
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
def dtypes_err_functions
|
177
|
+
str = <<SETFN
|
178
|
+
static void TypeErr(void) {
|
179
|
+
rb_raise(rb_eTypeError, "illegal operation with this type");
|
180
|
+
}
|
181
|
+
|
182
|
+
SETFN
|
183
|
+
end
|
184
|
+
|
185
|
+
def dtypes_function_name func, dtype_i, dtype_j = nil
|
186
|
+
if dtype_i[:enum] == :NM_NONE || (!dtype_j.nil? && dtype_j[:enum] == :NM_NONE)
|
187
|
+
str = "TypeErr"
|
188
|
+
else
|
189
|
+
str = func.to_s.camelize
|
190
|
+
str += "_#{dtype_i[:id]}"
|
191
|
+
str += "_#{dtype_j[:id]}" unless dtype_j.nil?
|
192
|
+
end
|
193
|
+
str
|
194
|
+
end
|
195
|
+
|
196
|
+
def dtypes_assign lhs, rhs
|
197
|
+
Generator::DTYPES_ASSIGN[ rhs.type ][ lhs.type ].call( lhs.sizeof, rhs.sizeof )
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
|
202
|
+
# Declare a set function for a pair of dtypes
|
203
|
+
def dtypes_set_function dtype_i, dtype_j
|
204
|
+
str = <<SETFN
|
205
|
+
static void #{dtypes_function_name(:set, dtype_i, dtype_j)}(size_t n, char* p1, size_t i1, char* p2, size_t i2) {
|
206
|
+
for (; n > 0; --n) {
|
207
|
+
#{dtypes_assign(dtype_i, dtype_j)}
|
208
|
+
p1 += i1; p2 += i2;
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
SETFN
|
213
|
+
end
|
214
|
+
|
215
|
+
def dtypes_increment_function dtype_i
|
216
|
+
str = <<INCFN
|
217
|
+
static void #{dtypes_function_name(:increment, dtype_i)}(void* p) { ++(*(#{dtype_i[:sizeof]}*)p); }
|
218
|
+
INCFN
|
219
|
+
end
|
220
|
+
|
221
|
+
def dtypes_upcast
|
222
|
+
ary = Array.new(15) { Array.new(15, nil) }
|
223
|
+
DTYPES.each_index do |a|
|
224
|
+
ad = DTYPES[a]
|
225
|
+
(a...DTYPES.size).each do |b|
|
226
|
+
bd = DTYPES[b]
|
227
|
+
|
228
|
+
entry = nil
|
229
|
+
|
230
|
+
if ad.type == :none || bd.type == :none
|
231
|
+
entry ||= 'NM_NONE'
|
232
|
+
elsif bd.type == ad.type
|
233
|
+
entry ||= DTYPES[[a,b].max].enum.to_s
|
234
|
+
elsif ad.type == :int # to float, complex, rational, or value
|
235
|
+
entry ||= DTYPES[[a,b].max].enum.to_s
|
236
|
+
elsif ad.enum == :NM_FLOAT32 # to complex or value
|
237
|
+
if [:NM_FLOAT64, :NM_COMPLEX64, :NM_COMPLEX128, :NM_ROBJ].include?(bd.enum)
|
238
|
+
entry ||= DTYPES[b].enum.to_s
|
239
|
+
elsif [:NM_RATIONAL32, :NM_RATIONAL64, :NM_RATIONAL128].include?(bd.enum)
|
240
|
+
entry ||= 'NM_FLOAT64'
|
241
|
+
else
|
242
|
+
entry ||= DTYPES[a].enum.to_s
|
243
|
+
end
|
244
|
+
elsif ad.enum == :NM_FLOAT64 # to complex or value
|
245
|
+
if [:NM_COMPLEX128, :NM_ROBJ].include?(bd.enum)
|
246
|
+
entry ||= DTYPES[b].enum.to_s
|
247
|
+
elsif bd.enum == :NM_COMPLEX64
|
248
|
+
entry ||= 'NM_COMPLEX128'
|
249
|
+
else
|
250
|
+
entry ||= DTYPES[a].enum.to_s
|
251
|
+
end
|
252
|
+
elsif ad.type == :rational # to float, complex, or value
|
253
|
+
if [:NM_FLOAT64, :NM_COMPLEX128, :NM_ROBJ].include?(bd.enum)
|
254
|
+
entry ||= DTYPES[b].enum.to_s
|
255
|
+
elsif bd.enum == :NM_FLOAT32
|
256
|
+
entry ||= 'NM_FLOAT64'
|
257
|
+
elsif bd.enum == :NM_COMPLEX64
|
258
|
+
entry ||= 'NM_COMPLEX128'
|
259
|
+
else
|
260
|
+
entry ||= DTYPES[a].enum.to_s
|
261
|
+
end
|
262
|
+
elsif ad.type == :complex
|
263
|
+
if bd.enum == :NM_ROBJ
|
264
|
+
entry ||= DTYPES[b].enum.to_s
|
265
|
+
else
|
266
|
+
entry ||= DTYPES[a].enum.to_s
|
267
|
+
end
|
268
|
+
elsif ad.type == :value # always value
|
269
|
+
entry ||= DTYPES[a].enum.to_s
|
270
|
+
end
|
271
|
+
|
272
|
+
ary[a][b] = ary[b][a] = entry
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
res = []
|
277
|
+
ary.each_index do |i|
|
278
|
+
res << "{ " + ary[i].join(", ") + " }"
|
279
|
+
end
|
280
|
+
decl("const int8_t Upcast[#{DTYPES.size}][#{DTYPES.size}] =", res) + "\n"
|
281
|
+
end
|
282
|
+
|
283
|
+
# binary-style functions, like Set (copy)
|
284
|
+
def dtypes_binary_functions_matrix func
|
285
|
+
ary = []
|
286
|
+
DTYPES.each do |i|
|
287
|
+
next if i[:enum] == :NM_TYPES
|
288
|
+
bry = []
|
289
|
+
DTYPES.each do |j|
|
290
|
+
next if j[:enum] == :NM_TYPES
|
291
|
+
bry << dtypes_function_name(func, i,j)
|
292
|
+
end
|
293
|
+
ary << "{ " + bry.join(", ") + " }"
|
294
|
+
end
|
295
|
+
ary
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
def dtypes_increment_functions_array
|
300
|
+
ary = []
|
301
|
+
DTYPES.each do |i|
|
302
|
+
next if i[:enum] == :NM_TYPES
|
303
|
+
if [:NM_INT8, :NM_INT16, :NM_INT32, :NM_INT64].include?(i.enum)
|
304
|
+
ary << dtypes_function_name(:increment, i)
|
305
|
+
else
|
306
|
+
ary << dtypes_function_name(:increment, DTYPES[0]) # TypeErr
|
307
|
+
end
|
308
|
+
end
|
309
|
+
ary
|
310
|
+
end
|
311
|
+
|
312
|
+
|
313
|
+
def dtypes_set_functions
|
314
|
+
ary = []
|
315
|
+
|
316
|
+
ary << dtypes_err_functions
|
317
|
+
|
318
|
+
DTYPES.each do |dtype_i|
|
319
|
+
DTYPES.each do |dtype_j|
|
320
|
+
begin
|
321
|
+
setfn = dtypes_set_function(dtype_i, dtype_j)
|
322
|
+
ary << setfn unless setfn =~ /TypeErr/
|
323
|
+
rescue NotImplementedError => e
|
324
|
+
STDERR.puts "Warning: #{e.to_s}"
|
325
|
+
rescue NoMethodError => e
|
326
|
+
# do nothing
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
ary << ""
|
331
|
+
ary << decl("nm_setfunc_t SetFuncs =", dtypes_binary_functions_matrix(:set))
|
332
|
+
|
333
|
+
ary.join("\n")
|
334
|
+
end
|
335
|
+
|
336
|
+
def dtypes_increment_functions
|
337
|
+
ary = []
|
338
|
+
|
339
|
+
DTYPES.each do |dtype_i|
|
340
|
+
next unless [:NM_INT8, :NM_INT16, :NM_INT32, :NM_INT64].include?(dtype_i.enum)
|
341
|
+
incfn = dtypes_increment_function(dtype_i)
|
342
|
+
ary << incfn unless incfn =~ /TypeErr/
|
343
|
+
end
|
344
|
+
|
345
|
+
ary << ""
|
346
|
+
ary << decl("nm_incfunc_t Increment =", dtypes_increment_functions_array)
|
347
|
+
|
348
|
+
ary.join("\n")
|
349
|
+
end
|
350
|
+
|
351
|
+
|
352
|
+
def dtypes_enum
|
353
|
+
decl("enum NMatrix_DTypes", DTYPES.map{ |d| d[:enum].to_s })
|
354
|
+
end
|
355
|
+
|
356
|
+
def dtypes_sizeof
|
357
|
+
decl("const int nm_sizeof[#{DTYPES.size}] =", DTYPES.map { |d| d[:sizeof].is_a?(Fixnum) ? d[:sizeof] : "sizeof(#{d[:sizeof].to_s})"})
|
358
|
+
end
|
359
|
+
|
360
|
+
def dtypes_typestring
|
361
|
+
decl("const char *nm_dtypestring[] =", DTYPES.map { |d| "\"#{d[:sym].to_s}\"" })
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
def make_file filename, &block
|
366
|
+
STDERR.puts "generated #{filename}"
|
367
|
+
f = File.new(filename, "w")
|
368
|
+
file_symbol = filename.split('.').join('_').upcase
|
369
|
+
|
370
|
+
f.puts "/* Automatically created using generator.rb - do not modify! */"
|
371
|
+
|
372
|
+
f.puts "#ifndef #{file_symbol}\n# define #{file_symbol}\n\n"
|
373
|
+
yield f
|
374
|
+
f.puts "\n#endif\n\n"
|
375
|
+
f.close
|
376
|
+
end
|
377
|
+
|
378
|
+
|
379
|
+
def make_dtypes_c
|
380
|
+
make_file "dtypes.c" do |f|
|
381
|
+
f.puts dtypes_sizeof
|
382
|
+
f.puts dtypes_typestring
|
383
|
+
f.puts dtypes_upcast
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
|
388
|
+
def make_dtypes_h
|
389
|
+
make_file "dtypes.h" do |f|
|
390
|
+
f.puts dtypes_enum
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
|
395
|
+
def make_dfuncs_c
|
396
|
+
make_file "dfuncs.c" do |f|
|
397
|
+
f.puts '#include <ruby.h>'
|
398
|
+
f.puts '#include "nmatrix.h"' + "\n\n"
|
399
|
+
f.puts dtypes_set_functions
|
400
|
+
f.puts dtypes_increment_functions
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
|
405
|
+
# Read templates given by +names+ from <tt>SRC_DIR/relative_path</tt>, and output them to a filename described by
|
406
|
+
# +output_name+.
|
407
|
+
#
|
408
|
+
# == Example
|
409
|
+
#
|
410
|
+
# make_templated_c './smmp', 'header', %w{numbmm transp bstoy ytobs}, "smmp1.c", {:TYPE => RATIONAL_DTYPES, :INT => INDEX_DTYPES}
|
411
|
+
#
|
412
|
+
# TODO: index dtype is unsigned!
|
413
|
+
# That means instead of int8_t, we should be doing uint8_t. But can't always do that because the Fortran code
|
414
|
+
# occasionally starts at index -1. Stupid Fortran! Someone needs to go through and fix the code by hand.
|
415
|
+
#
|
416
|
+
# TODO: Write tests to confirm that the signedness isn't screwing up stuff.
|
417
|
+
#
|
418
|
+
# TODO: Make templates work with complex and rational types too.
|
419
|
+
#
|
420
|
+
def make_templated_c relative_path, header_name, names, output_name, subs = {:TYPE => INDEX_DTYPES}
|
421
|
+
|
422
|
+
# First print the header once
|
423
|
+
`cat #{$RELATIVE_PATH}/#{SRC_DIR}/#{relative_path}/#{header_name}.template.c > ./#{output_name}` unless header_name.nil?
|
424
|
+
|
425
|
+
subs[:TYPE].each do |type|
|
426
|
+
if subs.has_key?(:INT)
|
427
|
+
subs[:INT].each do |int|
|
428
|
+
names.each do |name|
|
429
|
+
template "#{$RELATIVE_PATH}/#{SRC_DIR}/#{relative_path}/#{name}.template.c", output_name, :TYPE => type, :INT => int
|
430
|
+
end
|
431
|
+
end
|
432
|
+
else
|
433
|
+
names.each do |name|
|
434
|
+
template "#{$RELATIVE_PATH}/#{SRC_DIR}/#{relative_path}/#{name}.template.c", output_name, :TYPE => type
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def sub_int_real relative_path, name, output_name, int_dtype, real_dtype
|
441
|
+
cmd = ["#{$RELATIVE_PATH}/#{SRC_DIR}/#{relative_path}/#{name}.template.c",
|
442
|
+
"sed s/%%INT_ABBREV%%/#{int_dtype.id}/g",
|
443
|
+
"sed s/%%INT%%/#{int_dtype.sizeof}/g",
|
444
|
+
"sed s/%%INT_MAX%%/#{int_dtype.max_macro}/g",
|
445
|
+
"sed s/%%REAL_ABBREV%%/#{real_dtype.id}/g",
|
446
|
+
"sed s/%%REAL%%/#{real_dtype.sizeof}/g" ]
|
447
|
+
|
448
|
+
raise(ArgumentError, "no such template '#{name}'; looked at #{cmd[0]}") unless File.exist?(cmd[0])
|
449
|
+
|
450
|
+
`cat #{cmd.join(' | ')} >> ./#{output_name}`
|
451
|
+
end
|
452
|
+
|
453
|
+
|
454
|
+
def sub_int relative_path, name, output_name, dtype
|
455
|
+
cmd = ["#{$RELATIVE_PATH}/#{SRC_DIR}/#{relative_path}/#{name}.template.c",
|
456
|
+
"sed s/%%INT_ABBREV%%/#{dtype.id}/g",
|
457
|
+
"sed s/%%INT%%/#{dtype.sizeof}/g",
|
458
|
+
"sed s/%%INT_MAX%%/#{dtype.max_macro}/g"]
|
459
|
+
|
460
|
+
raise(ArgumentError, "no such template '#{name}'; looked at #{cmd[0]}") unless File.exist?(cmd[0])
|
461
|
+
|
462
|
+
`cat #{cmd.join(' | ')} >> ./#{output_name}`
|
463
|
+
end
|
464
|
+
|
465
|
+
|
466
|
+
# Evaluate one-line Ruby statements embedded in a template.
|
467
|
+
def gsub_yield line, t, dtype, line_number=nil, filename=nil
|
468
|
+
match = line.match YIELD_REGEX
|
469
|
+
while !match.nil?
|
470
|
+
|
471
|
+
statement = match[0][4...-2]
|
472
|
+
result = self.send :eval, statement, binding, filename, line_number
|
473
|
+
line["%%= #{statement}%%"] = result.to_s
|
474
|
+
|
475
|
+
match = line.match YIELD_REGEX
|
476
|
+
end
|
477
|
+
line
|
478
|
+
end
|
479
|
+
|
480
|
+
|
481
|
+
def gsub_expression_re re, line, t, dtype, line_number=nil, filename=nil
|
482
|
+
match = line.match re
|
483
|
+
while !match.nil?
|
484
|
+
expression = match[0][t.size+3...-2]
|
485
|
+
operation = SyntaxTree.parse(expression)
|
486
|
+
|
487
|
+
begin
|
488
|
+
operation_output = operation.operate(dtype.type, dtype.id)
|
489
|
+
|
490
|
+
# Correctly join together the lines of output operations and insert them into the template line
|
491
|
+
if operation.is_boolean?
|
492
|
+
line["%%#{t} #{expression}%%"] = operation_output[0]
|
493
|
+
else
|
494
|
+
line["%%#{t} #{expression}%%"] = operation_output.join(";\n") + ";"
|
495
|
+
end
|
496
|
+
|
497
|
+
rescue NotImplementedError => e
|
498
|
+
STDERR.puts "Error: #{e.inspect}"
|
499
|
+
raise(SyntaxError, "possible NotImplementedError (#{dtype.type}) in template #{filename}: #{line_number}: \"#{expression}\"")
|
500
|
+
rescue IndexError
|
501
|
+
raise(StandardError, "string not matched: '%%#{t} #{expression}%%'")
|
502
|
+
end
|
503
|
+
|
504
|
+
match = line.match re
|
505
|
+
end
|
506
|
+
line
|
507
|
+
end
|
508
|
+
|
509
|
+
|
510
|
+
# Replace a pseudo-mathematical expression with an actual one with dtypes taken into account.
|
511
|
+
def gsub_expression line, t, dtype, line_number=nil, filename=nil
|
512
|
+
gsub_expression_re /%%#{t}\ .*?%%/, line, t, dtype, line_number, filename
|
513
|
+
end
|
514
|
+
|
515
|
+
def gsub_expression_long line, t, dtype, line_number=nil, filename=nil
|
516
|
+
gsub_expression_re /%%#{t}_LONG\ .*?%%/, line, "#{t}_LONG", dtype.long_dtype, line_number, filename
|
517
|
+
end
|
518
|
+
|
519
|
+
|
520
|
+
# Replaces sub_int_real and sub_int.
|
521
|
+
#
|
522
|
+
# Allows more flexible substitutions. Pass a hash of templates, e.g., {:INT => INDEX_DTYPES[0], :REAL => RATIONAL_DTYPES[1]}, and
|
523
|
+
# it'll produce all possible combinations thereof.
|
524
|
+
#
|
525
|
+
# At some point we should probably just switch to erb. This just started growing and pretty soon I realized
|
526
|
+
# erb would likely have been a better option. Oh well.
|
527
|
+
def template template_filepath, output_filepath, types = {}
|
528
|
+
raise(ArgumentError, "expected substitution templates") if types.size == 0
|
529
|
+
|
530
|
+
#STDERR.puts "output_filepath = #{output_filepath}; Dir.pwd = #{Dir.pwd}"
|
531
|
+
|
532
|
+
output = File.new output_filepath, "a" # append
|
533
|
+
template = File.new template_filepath, "r"
|
534
|
+
|
535
|
+
line_count = 1
|
536
|
+
|
537
|
+
while line = template.gets
|
538
|
+
line.chomp!
|
539
|
+
|
540
|
+
types.each_pair do |t_sym,dtype|
|
541
|
+
t = t_sym.to_s
|
542
|
+
|
543
|
+
#STDERR.puts "Processing #{template_filepath}: #{line}"
|
544
|
+
if line.include?("%%#{t}")
|
545
|
+
line.gsub! "%%#{t}%%", dtype.sizeof.to_s
|
546
|
+
line.gsub! "%%#{t}_ABBREV%%", dtype.id.to_s
|
547
|
+
line.gsub! "%%#{t}_MAX%%", dtype.max_macro
|
548
|
+
line.gsub! "%%#{t}_LONG%%", dtype.long_dtype.sizeof.to_s #e.g., int64 instead of int8 for temporary variables
|
549
|
+
|
550
|
+
# Get any mathematical expressions that need to be translated
|
551
|
+
line = gsub_expression(line, t, dtype, line_count, template_filepath)
|
552
|
+
|
553
|
+
# Do the same for temp variables (which are often going to be more bytes)
|
554
|
+
line = gsub_expression_long(line, t, dtype, line_count, template_filepath)
|
555
|
+
end
|
556
|
+
|
557
|
+
# Deal with any Ruby statements in the template.
|
558
|
+
if line.include?("%%=")
|
559
|
+
line = gsub_yield(line, t, dtype, line_count, template_filepath)
|
560
|
+
end
|
561
|
+
|
562
|
+
end
|
563
|
+
|
564
|
+
line_count += 1
|
565
|
+
|
566
|
+
output.puts line
|
567
|
+
end
|
568
|
+
|
569
|
+
output.close
|
570
|
+
|
571
|
+
output_filepath
|
572
|
+
end
|
573
|
+
|
574
|
+
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
if $IN_MAKEFILE
|
579
|
+
Generator.make_dtypes_h
|
580
|
+
Generator.make_dtypes_c
|
581
|
+
Generator.make_dfuncs_c
|
582
|
+
Generator.make_templated_c './yale', 'smmp1_header', %w{smmp1}, 'smmp1.c', :TYPE => Generator::INDEX_DTYPES # 1-type interface functions for SMMP
|
583
|
+
Generator.make_templated_c './yale', nil, %w{smmp2}, 'smmp1.c', :TYPE => Generator::ACTUAL_DTYPES, :INT => Generator::INDEX_DTYPES # 2-type interface functions for SMMP
|
584
|
+
Generator.make_templated_c './yale', 'smmp2_header', %w{symbmm}, 'smmp2.c', :TYPE => Generator::INDEX_DTYPES # 1-type SMMP functions from Fortran
|
585
|
+
Generator.make_templated_c './yale', nil, %w{complexmath}, 'smmp2.c', :TYPE => Generator::COMPLEX_DTYPES
|
586
|
+
Generator.make_templated_c './yale', nil, %w{elementwise_op}, 'smmp2.c', :TYPE => Generator::ACTUAL_DTYPES
|
587
|
+
Generator.make_templated_c './yale', nil, %w{numbmm transp sort_columns elementwise}, 'smmp2.c', :TYPE => Generator::ACTUAL_DTYPES, :INT => Generator::INDEX_DTYPES # 2-type SMMP functions from Fortran and selection sort
|
588
|
+
Generator.make_templated_c './dense', 'blas_header', %w{rationalmath}, 'blas.partial.c', :TYPE => Generator::RATIONAL_DTYPES
|
589
|
+
Generator.make_templated_c './yale', nil, %w{complexmath}, 'blas.partial.c', :TYPE => Generator::COMPLEX_DTYPES
|
590
|
+
Generator.make_templated_c './dense', nil, %w{gemm gemv}, 'blas.partial.c', :TYPE => Generator::NONBLAS_DTYPES
|
591
|
+
Generator.make_templated_c './dense', nil, %w{elementwise}, 'blas.partial.c', :TYPE=>Generator::ACTUAL_DTYPES
|
592
|
+
`cat blas.partial.c >> blas.c`
|
593
|
+
`rm blas.partial.c`
|
594
|
+
end
|