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,481 @@
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
+ # == syntax_tree.rb
24
+ #
25
+ # Syntax Tree for mathematical expression parsing. Produces the
26
+ # correct forms for each type in NMatrix. Used by the C templates
27
+ # in generator.rb.
28
+ #
29
+
30
+ class Identifier < String
31
+ def operate type=nil,exact_type=nil
32
+ [type == :value || type == :object && to_s == "0" ? "RUBY_ZERO" : to_s]
33
+ end
34
+
35
+ def depth; 0; end
36
+
37
+ # These are just dummy functions which alias to_s
38
+
39
+ def operate_complex exact_type=nil
40
+ [to_s]
41
+ end
42
+
43
+ def operate_rational exact_type=nil
44
+ [to_s]
45
+ end
46
+
47
+ def operate_value exact_type=nil
48
+ [to_s]
49
+ end
50
+ end
51
+
52
+
53
+ class SyntaxTree
54
+ EQ = :'='
55
+ # Changing the order of these arrays will change the relative precedence of the operators.
56
+ ASSIGN_OPS = %w{+= -= *= /= %= &= ^= |= >>= <<=}.map { |o| o.intern }
57
+ COMP_OPS = %w{<= >= == != < >}.map { |o| o.intern }
58
+ TRANSITIVE_BINARY_OPS = %w{&& || & ~ + *}.map { |o| o.intern }
59
+ BINARY_OPS = %w{>> << && || & | ^ - + * / %}.map { |o| o.intern }
60
+ BITWISE_BINARY_OPS = %w{& | ^}.map { |o| o.intern }
61
+ UNARY_OPS = %w{~ - !}
62
+ DISALLOWED_OPS = %w{++ --}.map { |o| o.intern }
63
+ OPS = (%w{>> <<}.concat(ASSIGN_OPS + [EQ]).concat(COMP_OPS).concat(%w{- + * / % & | ^}).concat(UNARY_OPS)).map { |o| o.intern } # skipping: >> << && || & | ~
64
+
65
+ FLIP_OPS = {:'<=' => :'>=',
66
+ :'>=' => :'<=',
67
+ :'==' => :'==',
68
+ :'!=' => :'!=',
69
+ :'>' => :'<',
70
+ :'<' => :'>' }
71
+
72
+ # Generally this is only used internally. Instead, call SyntaxTree.parse(operation), which will
73
+ # in turn correctly call SyntaxTree::initialize.
74
+ def initialize op, left, right
75
+ if left == "0" && COMP_OPS.include?(op) # Flip certain operations so that the 0 is on the right.
76
+ @op = FLIP_OPS[op]
77
+ @left = right
78
+ @right = left
79
+ elsif left.is_a?(String) && left.size == 0
80
+ @op = op
81
+ @left = nil
82
+ @right = right
83
+ else
84
+ @op = op
85
+ @left = left
86
+ @right = right
87
+ end
88
+ end
89
+ attr_reader :op
90
+ attr_accessor :left, :right
91
+
92
+
93
+ def unary?
94
+ left.nil?
95
+ end
96
+
97
+
98
+ def is_boolean?
99
+ COMP_OPS.include?(op)
100
+ end
101
+
102
+
103
+ # Flip an operation, if possible. If not, do nothing.
104
+ def reverse!
105
+ if COMP_OPS.include?(op)
106
+ @op = FLIP_OPS[op]
107
+ tmp = @left
108
+ @left = @right
109
+ @right = tmp
110
+ elsif TRANSITIVE_BINARY_OPS.include?(op)
111
+ tmp = @left
112
+ @left = @right
113
+ @right = tmp
114
+ end
115
+ end
116
+
117
+ # Flip an operation
118
+ def reverse
119
+ self.dup.reverse!
120
+ end
121
+
122
+
123
+ # Expand the syntax tree in place on ASSIGN_OPS. If @op is not one of the ASSIGN_OPS, do nothing.
124
+ def expand!
125
+ if ASSIGN_OPS.include?(@op)
126
+ old_op = @op
127
+ @op = EQ
128
+ old_right = @right
129
+ @right = SyntaxTree.new(old_op.to_s[0...old_op.size-1].intern, @left, old_right)
130
+ end
131
+ self
132
+ end
133
+
134
+
135
+ # Copy and expand the syntax tree on ASSIGN_OPS. e.g., += becomes = and +
136
+ def expand
137
+ self.dup.expand!
138
+ end
139
+
140
+
141
+ def depth
142
+ unary? ? right.depth : (left.depth > right.depth ? left.depth : right.depth) + 1
143
+ end
144
+
145
+ # Split the SyntaxTree into a whole bunch of simpler ones appropriate for doing struct-type operations.
146
+ def simplify_split! count=1
147
+ expand!
148
+ ary = [self]
149
+ if op == EQ
150
+ if depth <= 2
151
+ ary
152
+ else
153
+ ary
154
+ if right.left.is_a?(SyntaxTree)
155
+ old_right_left = right.left
156
+ right.left = Identifier.new("temp#{count}")
157
+ new_right_left = SyntaxTree.new(EQ, right.left, old_right_left)
158
+ count += 1
159
+ ary = ary.concat(new_right_left.simplify_split!(count))
160
+ end
161
+
162
+ if right.right.is_a?(SyntaxTree)
163
+ old_right_right = right.right
164
+ right.right = Identifier.new("temp#{count}")
165
+ new_right_right = SyntaxTree.new(EQ, right.right, old_right_right)
166
+ count += 1
167
+ ary = ary.concat(new_right_right.simplify_split!(count))
168
+ end
169
+
170
+ ary.reverse
171
+ end
172
+ else
173
+ ary
174
+ end
175
+ end
176
+
177
+ def simplify_split count=1
178
+ dup.simplify_split! count
179
+ end
180
+
181
+
182
+ # Deep copy.
183
+ def dup
184
+ SyntaxTree.new(op, (unary? ? nil : left.dup), right.dup)
185
+ end
186
+
187
+
188
+ # Split tree into multiple simpler subtrees and figure out the appropriate operation on each of those.
189
+ #
190
+ # Only needed for rational and complex, which are structs and can't rely on basic operators like * and +.
191
+ def operate_on_subtrees type, exact_type
192
+ i = 0
193
+ best_type = begin
194
+ case type
195
+ when :rational
196
+ :r128
197
+ when :complex
198
+ :c128
199
+ when :object
200
+ :v
201
+ end
202
+ end
203
+ subtrees = simplify_split
204
+ subtrees.map do |subtree|
205
+ i += 1
206
+ i == subtrees.size ? subtree.send("operate_#{type}", exact_type) : subtree.send("operate_#{type}", best_type)
207
+ end
208
+ end
209
+
210
+
211
+ def operate type, exact_type=nil
212
+ operations =
213
+ case type
214
+ when :int, :float
215
+ operate_simple(type, exact_type)
216
+ when :rational, :complex, :value
217
+ operate_on_subtrees(type, exact_type).flatten
218
+ else
219
+ raise ArgumentError, "undefined math expression type '#{type}'"
220
+ end
221
+
222
+ # Separate boolean operations by && or ; as appropriate for the type of operation.
223
+ # This is overly simplistic, and won't work for things like x = y && z; but, it will
224
+ # work for the majority of cases we need to deal with in our templates.
225
+ #COMP_OPS.include?(op) ? operations.join(" && ") : operations.join(";\n") + ";"
226
+ operations
227
+ end
228
+
229
+
230
+ protected
231
+ def is_expression_boolean? expr
232
+ expr =~ /^\(.*(==|!=|>=|<=| < | > |&&|\|\|)+.*\)$/ || expr =~ /^!/
233
+ end
234
+
235
+
236
+ def operate_simple type, exact_type
237
+ if unary?
238
+ if op == :'~' && type == :float
239
+ ["rb_raise(rb_eSyntaxError, \"cannot perform bitwise operations on floats\")"]
240
+ else
241
+ ["#{op}#{right.operate(type, exact_type).first}"]
242
+ end
243
+ elsif op == EQ
244
+ r = right.operate(type, exact_type).first
245
+ r =~ /^rb_raise\(rb_eSyntaxError/ ? [r] : ["#{left} = #{r}"]
246
+ elsif op == :'%' && type == :float
247
+ ["fmod(#{left.operate(type, exact_type).first}, #{right.operate(type, exact_type).first})"]
248
+ elsif [:'&', :'|', :'^', :'<<', :'>>'].include?(op)
249
+ ["rb_raise(rb_eSyntaxError, \"cannot perform bitwise operations on floats\")"]
250
+ else
251
+ ["#{left.operate(type, exact_type).first} #{op} #{right.operate(type, exact_type).first}"]
252
+ end
253
+ end
254
+
255
+ # Figure out what to do on the right side of the operation if we're dealing with Ruby objects and unary operators
256
+ def operate_value_right
257
+ if !right.is_a?(SyntaxTree) && right.to_i.to_s == right
258
+ unary? ? "INT2FIX(#{op}#{right})" : "INT2FIX(#{right})"
259
+ elsif right.is_a?(SyntaxTree) && right.unary? && right.right.to_i.to_s == right.right
260
+ "INT2FIX(#{op}#{right})"
261
+ else
262
+ right.operate_value.first
263
+ end
264
+ end
265
+
266
+ def operate_value exact_type=nil # exact_type is not used.
267
+ if unary?
268
+ ["rb_funcall(#{operate_value_right}, rb_intern(\"#{op}@\"), 0)"] # e.g., -@ for negative
269
+ else
270
+ case op
271
+ when EQ
272
+ ["#{left} #{op} #{operate_value_right}"]
273
+ when *COMP_OPS
274
+ ["rb_funcall(#{left.operate_value.first}, rb_intern(\"#{op}\"), 1, #{operate_value_right}) == Qtrue"]
275
+ else
276
+ ["rb_funcall(#{left.operate_value.first}, rb_intern(\"#{op}\"), 1, #{operate_value_right})"]
277
+ end
278
+ end
279
+ end
280
+
281
+ def operate_rational exact_type=:r128
282
+ if unary?
283
+ if op == :'-'
284
+ ["#{exact_type}_negate(#{right}.n, #{right}.d)"]
285
+ elsif op == :'~'
286
+ ["rb_raise(rb_eSyntaxError, \"cannot perform bitwise operations on rational numbers\")"]
287
+ elsif op == :'!'
288
+ ["#{exact_type}_bang(#{right}.n, #{right}.d)"]
289
+ else
290
+ raise NotImplementedError, "unhandled unary operation #{op} on complex and 0"
291
+ end
292
+ elsif right == "0"
293
+ if op == EQ
294
+ ["#{left}.n = 0", " #{left}.d = 1"]
295
+ elsif COMP_OPS.include?(op)
296
+ ["#{left}.n #{op} 0"]
297
+ else
298
+ raise NotImplementedError, "unhandled operation #{op} on rational and 0"
299
+ end
300
+ elsif right == "1"
301
+ if op == EQ
302
+ ["#{left}.n = #{left}.d = 1"]
303
+ elsif COMP_OPS.include?(op)
304
+ ["#{left}.n #{op} #{left}.d"]
305
+ elsif [:'+', :'-'].include?(op)
306
+ ["#{exact_type}_addsub(#{left}.n, #{left}.d, #{left}.d, #{left}.d, '#{op}')"]
307
+ else
308
+ raise NotImplementedError, "unhandled operation #{op} on rational and 1"
309
+ end
310
+ else
311
+ if op == EQ
312
+ r = right.operate_rational(exact_type).first
313
+ if r =~ /^rb_raise\(rb_eSyntaxError/
314
+ [r]
315
+ elsif is_expression_boolean?(r) # x = boolean: can't assign directly.
316
+ ["#{left} = BOOL2#{exact_type.to_s.upcase}(#{r})"]
317
+ else
318
+ ["#{left} = #{right.operate_rational(exact_type).first}"]
319
+ end
320
+ elsif op == :'=='
321
+ ["(#{left}.n == #{right}.n && #{left}.d == #{right}.d)"]
322
+ elsif COMP_OPS.include?(op)
323
+ ["(#{left}.n / (double)(#{left}.d) #{op} #{right}.n / (double)(#{right}.d))"] # TODO: Is there a faster way?
324
+ elsif [:'+', :'-'].include?(op)
325
+ ["#{exact_type}_addsub(#{left}.n, #{left}.d, #{right}.n, #{right}.d, '#{op}')"]
326
+ elsif [:'*', :'/'].include?(op)
327
+ ["#{exact_type}_muldiv(#{left}.n, #{left}.d, #{right}.n, #{right}.d, '#{op}')"]
328
+ elsif op == :'%'
329
+ ["#{exact_type}_mod(#{left}.n, #{left}.d, #{right}.n, #{right}.d)"]
330
+ elsif op == :'<<'
331
+ ["rb_raise(rb_eSyntaxError, \"cannot perform bitwise operations on rational numbers\")"]
332
+ elsif op == :'>>'
333
+ ["rb_raise(rb_eSyntaxError, \"cannot perform bitwise operations on rational numbers\")"]
334
+ elsif BITWISE_BINARY_OPS.include?(op)
335
+ ["rb_raise(rb_eSyntaxError, \"cannot perform bitwise operations on rational numbers\")"]
336
+ else
337
+ raise NotImplementedError, "unhandled operation #{op} on rationals"
338
+ end
339
+ end
340
+ end
341
+
342
+
343
+ def operate_complex_boolean_helper exact_type, real_comp, join = :'&&', imag_comp=nil
344
+ imag_comp ||= real_comp
345
+ ["(#{left.operate_complex(exact_type).first}.r #{real_comp} #{right.operate_complex(exact_type).first}.r && #{left.operate_complex(exact_type).first}.i #{imag_comp} #{right.operate_complex(exact_type).first}.i)"]
346
+ end
347
+
348
+
349
+ def operate_complex exact_type=:c128
350
+ if unary?
351
+ if op == :'-'
352
+ ["#{exact_type}_negate(#{right}.r, #{right}.i)"]
353
+ elsif op == :'~'
354
+ ["rb_raise(rb_eSyntaxError, \"cannot perform bitwise operations on complex numbers\")"]
355
+ elsif op == :'!'
356
+ ["#{exact_type}_bang(#{right}.r, #{right}.i)"]
357
+ else
358
+ raise NotImplementedError, "unhandled unary operation #{op} on complex and 0"
359
+ end
360
+ elsif right == "0"
361
+ if op == EQ
362
+ ["#{left}.r = 0", " #{left}.i = 0"]
363
+ elsif op == :'=='
364
+ ["(#{left}.r == 0 && #{left}.i == 0)"]
365
+ elsif op == :'!='
366
+ ["(#{left}.r != 0 || #{left}.i != 0)"]
367
+ elsif op == :'>'
368
+ ["(#{left}.r > 0 && #{left}.i > 0)"]
369
+ elsif op == :'<'
370
+ ["(#{left}.r < 0 && #{left}.i < 0)"]
371
+ elsif op == :'>='
372
+ ["(!(#{left}.r < 0 || #{left}.i < 0)"]
373
+ elsif op == :'<='
374
+ ["(!(#{left}.r > 0 || #{left}.i > 0)"]
375
+ else
376
+ raise NotImplementedError, "unhandled operation #{op} on complex and 0"
377
+ end
378
+ else
379
+ if op == EQ
380
+ r = right.operate_complex(exact_type).first
381
+ if r =~ /^rb_raise\(rb_eSyntaxError/
382
+ [r]
383
+ elsif is_expression_boolean?(r) # x = boolean: can't assign directly.
384
+ ["#{left} = BOOL2#{exact_type.to_s.upcase}(#{r})"]
385
+ else
386
+ ["#{left} = #{r}"]
387
+ end
388
+ elsif op == :'=='
389
+ operate_complex_boolean_helper(exact_type, op)
390
+ elsif op == :'!='
391
+ operate_complex_boolean_helper(exact_type, op, :'||')
392
+ elsif op == :'>'
393
+ operate_complex_boolean_helper(exact_type, op)
394
+ elsif op == :'<'
395
+ operate_complex_boolean_helper(exact_type, op)
396
+ elsif op == :'>='
397
+ operate_complex_boolean_helper(exact_type, op)
398
+ elsif op == :'<='
399
+ operate_complex_boolean_helper(exact_type, op)
400
+ elsif op == :'+'
401
+ ["#{exact_type}_add(#{left}.r, #{left}.i, #{right}.r, #{right}.i)"]
402
+ elsif op == :'-'
403
+ ["#{exact_type}_sub(#{left}.r, #{left}.i, #{right}.r, #{right}.i)"]
404
+ elsif op == :'*'
405
+ ["#{exact_type}_mul(#{left}.r, #{left}.i, #{right}.r, #{right}.i)"]
406
+ elsif op == :'/'
407
+ ["#{exact_type}_div(#{left}.r, #{left}.i, #{right}.r, #{right}.i)"]
408
+ elsif op == :'%'
409
+ ["#{exact_type}_mod(#{left}.r, #{left}.i, #{right}.r, #{right}.i)"]
410
+ elsif [:'&', :'|', :'^', :'<<', :'>>'].include?(op)
411
+ ["rb_raise(rb_eSyntaxError, \"cannot perform bitwise operations on complex numbers\")"]
412
+ elsif op == :'!'
413
+ ["0"]
414
+ else
415
+ STDERR.puts "WARNING: unhandled operation #{op} on complex numbers"
416
+ ["0"]
417
+ end
418
+ end
419
+ end
420
+
421
+ public
422
+ class << self
423
+
424
+ # Parse an operation template of some kind into a SyntaxTree or, if no operation present,
425
+ # into an Identifier (basically a string)
426
+ def parse str
427
+ SyntaxTree::OPS.each do |op_symbol|
428
+ op = op_symbol.to_s
429
+ pos = str.index(op, 1)
430
+
431
+ while !pos.nil? # Need to look for each operator multiple times in a line
432
+
433
+ raise(ArgumentError, "disallowed operation '#{op}' in expression") if DISALLOWED_OPS.include?(op_symbol)
434
+
435
+ # Is the operator contained within brackets?
436
+ lb = str.rindex('[', pos)
437
+ unless lb.nil?
438
+ rb = str.rindex(']', pos) # Perhaps that bracket is terminated?
439
+ rb = str.index(']', pos+op.size) if rb.nil? || rb < lb # still have to worry; try looking after the operator
440
+
441
+ if BINARY_OPS.include?(op_symbol) && !rb.nil? && lb < pos && rb >= pos
442
+ # Operator IS within brackets. Let's start searching again after the brackets.
443
+ #STDERR.puts "operator #{op_symbol} was found, but within brackets (#{lb}, #{pos}, #{rb}) '#{str}'"
444
+ pos = str.index(op, rb+1)
445
+ next
446
+ end
447
+ end
448
+
449
+ # If we get this far, the operator was not found within brackets.
450
+
451
+ # Split around the operator
452
+ left = str[0...pos].strip
453
+ right = str[pos+op.size...str.size].strip
454
+
455
+ # We don't want to process x <= y as 'x <' = 'y', but as 'x' <= 'y':
456
+ if op_symbol == EQ
457
+ if SyntaxTree::OPS.include?((left[-1]+'=').intern)
458
+ pos = str.index(op, pos+1)
459
+ next
460
+ elsif SyntaxTree::OPS.include?(('=' + right[0]).intern) # Same for ==
461
+ pos = str.index(op, pos+2)
462
+ next
463
+ end
464
+ end
465
+
466
+ STDERR.puts "operator #{op_symbol} was found (#{pos}); parsing '#{left}' and '#{right}'"
467
+ return SyntaxTree.new(op_symbol, SyntaxTree.parse(left), SyntaxTree.parse(right))
468
+
469
+ end
470
+ end
471
+
472
+ if str =~ /[~\-!]/
473
+ STDERR.puts "unary operator #{str[0]} was found (0); making identifier of #{str[1...str.size]}"
474
+ SyntaxTree.new(str[0].intern, "", Identifier.new(str[1...str.size]))
475
+ else
476
+ STDERR.puts "Making identifier out of #{str}"
477
+ Identifier.new(str)
478
+ end
479
+ end
480
+ end
481
+ end