multiarray 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -6,11 +6,11 @@ require 'rake/packagetask'
6
6
  require 'rbconfig'
7
7
 
8
8
  PKG_NAME = 'multiarray'
9
- PKG_VERSION = '0.4.0'
9
+ PKG_VERSION = '0.4.1'
10
10
  RB_FILES = FileList[ 'lib/**/*.rb' ]
11
11
  TC_FILES = FileList[ 'test/tc_*.rb' ]
12
12
  TS_FILES = FileList[ 'test/ts_*.rb' ]
13
- PKG_FILES = [ 'Rakefile', 'README', 'COPYING', '.document' ] +
13
+ PKG_FILES = [ 'Rakefile', 'README', 'COPYING', 'TODO', '.document' ] +
14
14
  RB_FILES + TS_FILES + TC_FILES
15
15
  SUMMARY = %q{Multi-dimensional and uniform Ruby arrays}
16
16
  DESCRIPTION = %q{This Ruby-extension defines Hornetseye::MultiArray and other native datatypes. Hornetseye::MultiArray provides multi-dimensional Ruby arrays with elements of same type. The extension is designed to be mostly compatible with Masahiro Tanaka's NArray. However it allows the definition of custom element types and operations on them. This work was also inspired by Ronald Garcia's boost::multi_array and by Todd Veldhuizen's Blitz++.}
data/TODO ADDED
@@ -0,0 +1,63 @@
1
+ ranges (rolling, lazy rolling, lazy ranges)
2
+ tensor indices to enable transpose of lazy array
3
+ test lazyness
4
+ sums/injections? nesting? tensors?
5
+ JIT?
6
+ test type conversions
7
+ fancy README
8
+
9
+ a = lazy( 16 ) { |i| i }
10
+ a = lazy( 16 ) { |i| i }[ 4 ... 12 ] # offsets: apply sel-operation to all
11
+ members; index array?
12
+ a = lazy( 16, 32 ) { |i,j| Sequence[ i, j ] } # ???
13
+ a = lazy( 8, 8 ) { |i,j| Sequence[ i, a[j] * sin( i + b[j] ) ] }.hist 8, 8
14
+ a = b.class.new.op { |x| set -x }
15
+ a = lazy { |i| -b[i] }
16
+ a = lazy { -b }
17
+ a = array { -b }
18
+ a = array { |i| -b[i] }
19
+ a = array( :dim => [ b.size ] ) { |i| -b[i] }
20
+ a = -b
21
+ array { |i| sum { |j| a[i,j] } }
22
+ lazy { |i| sum { |j| a[i,j] } }
23
+ parallel { ... }
24
+ lazy { |i| lazy { |j| a[i,j] } }
25
+ array { lazy { |i| sum { |j| a[i,j] } } }
26
+
27
+ correlate?
28
+
29
+ lines:
30
+ [ i, a[j] * sin( i + b[j] ) ].hist
31
+
32
+ lines:
33
+ lazy { |i,j| i.zip( a[j] * sin( i + b[j] ) ) }.histogram 32, 20
34
+
35
+ lines:
36
+ lazy { |i,j| Sequence[ i, a[j] * sin( i + b[j] ) ] }.histogram 32, 20
37
+ combined histograms?
38
+
39
+ circle:
40
+ [ sin( i ) * r, cos( i ) * r ].hist
41
+
42
+ Geometric Hashing? Bounded Hough Transform!!! RANSAC? histogram weights?
43
+
44
+ array: lazy retrieval? jit code for address computation
45
+ OBJECT: object with type information
46
+ Lazy( Sequence( INT, 3 ) )
47
+
48
+ interpretation: type coercion, actual operation, jit,
49
+ collection of jit arguments (e.g. tensor)
50
+
51
+ proc { |i| proc { |j| i+j } }.call( 5 ).call 3
52
+
53
+ gem install flay: http://ruby.sadi.st/Flay.html
54
+
55
+ How to nest/cascade mode-environments?
56
+ (how to specify nested modes for recursive algorithms and called algorithms?)
57
+ ruby and jit compiles, lazy and parallel forwards
58
+
59
+ * Ruby
60
+ * Lazy (Lazy histogram -> hough transform, lazy transpose, unused indices?)
61
+ * Multithreading
62
+ * JIT
63
+ * GCC
@@ -37,6 +37,40 @@ class Object
37
37
 
38
38
  end
39
39
 
40
+ # Range#min and Range#max are replaced for performance reasons
41
+ class Range
42
+
43
+ public
44
+
45
+ alias_method :orig_min, :min
46
+
47
+ alias_method :orig_max, :max
48
+
49
+ # For performance reasons a specialised method for integers is added
50
+ def min
51
+ if self.begin.is_a? Integer
52
+ self.begin
53
+ else
54
+ orig_min
55
+ end
56
+ end
57
+
58
+ # For performance reasons a specialised method for integers is added.
59
+ def max
60
+ if self.end.is_a? Integer
61
+ exclude_end? ? self.end - 1 : self.end
62
+ else
63
+ orig_max
64
+ end
65
+ end
66
+
67
+ # Compute the size of a range.
68
+ def size
69
+ max + 1 - min
70
+ end
71
+
72
+ end
73
+
40
74
  # Module#alias_method_chain is defined.
41
75
  #
42
76
  # @private
@@ -120,6 +120,22 @@ module Hornetseye
120
120
  ( bits + 7 ).div 8
121
121
  end
122
122
 
123
+ def coercion( other )
124
+ if other < INT_
125
+ Hornetseye::INT [ bits, other.bits ].max, ( signed or other.signed )
126
+ else
127
+ super other
128
+ end
129
+ end
130
+
131
+ def coerce( other )
132
+ if other < INT_
133
+ return other, self
134
+ else
135
+ super other
136
+ end
137
+ end
138
+
123
139
  def ==( other )
124
140
  if other.is_a? Class
125
141
  if other < INT_ and bits == other.bits and signed == other.signed
@@ -147,6 +163,36 @@ module Hornetseye
147
163
  self
148
164
  end
149
165
 
166
+ module RubyMatching
167
+
168
+ def fit( *values )
169
+ if values.all? { |value| value.is_a? Integer }
170
+ bits = 8
171
+ ubits = 8
172
+ signed = false
173
+ values.each do |value|
174
+ bits *= 2 until ( -2**(bits-1) ... 2**(bits-1) ).include? value
175
+ if value < 0
176
+ signed = true
177
+ else
178
+ ubits *= 2 until ( 0 ... 2**ubits ).include? value
179
+ end
180
+ end
181
+ bits = signed ? bits : ubits
182
+ if bits <= 64
183
+ Hornetseye::INT bits, signed
184
+ else
185
+ super *values
186
+ end
187
+ else
188
+ super *values
189
+ end
190
+ end
191
+
192
+ end
193
+
194
+ Type.extend RubyMatching
195
+
150
196
  end
151
197
 
152
198
  end
@@ -29,8 +29,13 @@ module Hornetseye
29
29
  end
30
30
 
31
31
  def element( index )
32
- elements = @values.collect { |value| value.element index }
33
- Lazy.new( *( elements + [ :action => @action ] ) )
32
+ values = @values.collect { |value| value.element index }
33
+ Lazy.new( *( values + [ :action => @action ] ) )
34
+ end
35
+
36
+ def elements( range )
37
+ values = @values.collect { |value| value.elements range }
38
+ Lazy.new( *( values + [ :action => @action ] ) )
34
39
  end
35
40
 
36
41
  def -@
@@ -22,19 +22,7 @@ module Hornetseye
22
22
  end
23
23
 
24
24
  def []( *args )
25
- probe = proc do |s,a|
26
- if a.is_a? Array
27
- if s.empty?
28
- a.inject( [], &probe ) + [ a.size ]
29
- else
30
- a.inject( s[ 0 ... -1 ], &probe ) + [ [ a.size, s.last ].max ]
31
- end
32
- else
33
- s
34
- end
35
- end
36
- shape = probe.call [], args
37
- retval = MultiArray.new OBJECT, *shape
25
+ retval = Type.fit( args ).new
38
26
  retval[] = args
39
27
  retval
40
28
  end
@@ -48,6 +48,18 @@ module Hornetseye
48
48
  1
49
49
  end
50
50
 
51
+ def coercion( other )
52
+ if other < Pointer_
53
+ other.coercion self
54
+ else
55
+ self
56
+ end
57
+ end
58
+
59
+ def coerce( other )
60
+ return self, self
61
+ end
62
+
51
63
  end
52
64
 
53
65
  def store( ptr )
@@ -55,16 +67,18 @@ module Hornetseye
55
67
  self
56
68
  end
57
69
 
58
- end
70
+ raise '\'multiarray/object\' must be loaded first' if Type.respond_to? :fit
59
71
 
60
- module RubyMatching
72
+ module RubyMatching
73
+
74
+ def fit( *values )
75
+ OBJECT
76
+ end
61
77
 
62
- def fit( *value )
63
- OBJECT
64
78
  end
65
79
 
66
- end
80
+ Type.extend RubyMatching
67
81
 
68
- Type.extend RubyMatching
82
+ end
69
83
 
70
84
  end
@@ -42,6 +42,26 @@ module Hornetseye
42
42
  primitive.dereference
43
43
  end
44
44
 
45
+ def to_type( typecode, options = {} )
46
+ Hornetseye::Pointer primitive.to_type( typecode, options )
47
+ end
48
+
49
+ def coercion( other )
50
+ if other < Pointer_
51
+ Hornetseye::Pointer primitive.coercion( other.primitive )
52
+ else
53
+ Hornetseye::Pointer primitive.coercion( other )
54
+ end
55
+ end
56
+
57
+ def coerce( other )
58
+ if other < Pointer_
59
+ return other, self
60
+ else
61
+ super other
62
+ end
63
+ end
64
+
45
65
  end
46
66
 
47
67
  def inspect( indent = nil, lines = nil )
@@ -119,7 +139,12 @@ module Hornetseye
119
139
  end
120
140
  else
121
141
  index = args.pop
122
- element( index )[ *args ]
142
+ case index
143
+ when Range
144
+ elements( index )[ *args ]
145
+ else
146
+ element( index )[ *args ]
147
+ end
123
148
  end
124
149
  end
125
150
 
@@ -182,13 +207,25 @@ module Hornetseye
182
207
  end
183
208
 
184
209
  def element( index )
210
+ target = Hornetseye::Pointer self.class.primitive.element_type
185
211
  if @value.is_a? Lazy
186
- Hornetseye::Pointer( self.class.primitive.element_type ).
187
- new @value.element( index )
212
+ target.new @value.element( index )
188
213
  else
189
214
  pointer = @value + index * self.class.primitive.stride *
190
215
  self.class.primitive.typecode.storage_size
191
- Hornetseye::Pointer( self.class.primitive.element_type ).new pointer
216
+ target.new pointer
217
+ end
218
+ end
219
+
220
+ def elements( range )
221
+ target = Sequence self.class.primitive.element_type, range.size,
222
+ self.class.primitive.stride
223
+ if @value.is_a? Lazy
224
+ target.new @value.elements( range )
225
+ else
226
+ pointer = @value + range.min * self.class.primitive.stride *
227
+ self.class.primitive.typecode.storage_size
228
+ target.new pointer
192
229
  end
193
230
  end
194
231
 
@@ -21,7 +21,11 @@ module Hornetseye
21
21
  end
22
22
 
23
23
  def []( *args )
24
- retval = Hornetseye::Sequence( OBJECT, args.size ).new
24
+ target = Type.fit args
25
+ if target.primitive.dimension > 1
26
+ target = Hornetseye::Sequence OBJECT, args.size
27
+ end
28
+ retval = target.new
25
29
  retval[] = args
26
30
  retval
27
31
  end
@@ -93,12 +93,63 @@ module Hornetseye
93
93
  element_type.shape + [ num_elements ]
94
94
  end
95
95
 
96
+ def dimension
97
+ element_type.dimension.succ
98
+ end
99
+
96
100
  def storage_size
97
101
  element_type.storage_size * num_elements
98
102
  end
99
103
 
104
+ def to_type( typecode, options = {} )
105
+ options = { :preserve_strides => false }.merge options
106
+ target_element = element_type.to_type typecode, options
107
+ target_stride = options[ :preserve_strides ] ?
108
+ stride : target_element.size
109
+ Hornetseye::Sequence( target_element, num_elements,
110
+ target_stride ).dereference
111
+ end
112
+
113
+ def coercion( other )
114
+ if other < Sequence_
115
+ Hornetseye::Sequence( element_type.coercion( other.element_type ),
116
+ num_elements ).primitive
117
+ else
118
+ Hornetseye::Sequence( element_type.coercion( other ),
119
+ num_elements ).primitive
120
+ end
121
+ end
122
+
123
+ def coerce( other )
124
+ if other < Sequence_
125
+ return other, self
126
+ else
127
+ return Hornetseye::Sequence( other, num_elements ), self
128
+ end
129
+ end
130
+
100
131
  end
101
132
 
133
+ module RubyMatching
134
+
135
+ def fit( *values )
136
+ n = values.inject( 0 ) do |size,value|
137
+ value.is_a?( Array ) ? [ size, value.size ].max : size
138
+ end
139
+ if n > 0
140
+ subvalues = values.inject( [] ) do |flat,value|
141
+ flat + ( value.is_a?( Array ) ? value : [ value ] )
142
+ end
143
+ Hornetseye::Sequence fit( *subvalues ), n
144
+ else
145
+ super *values
146
+ end
147
+ end
148
+
149
+ end
150
+
151
+ Type.extend RubyMatching
152
+
102
153
  end
103
154
 
104
155
  end
@@ -35,6 +35,10 @@ module Hornetseye
35
35
  []
36
36
  end
37
37
 
38
+ def dimension
39
+ 0
40
+ end
41
+
38
42
  # Number of elements
39
43
  #
40
44
  # @return [Integer] Number of elements
@@ -44,6 +48,27 @@ module Hornetseye
44
48
  shape.inject( 1 ) { |a,b| a * b }
45
49
  end
46
50
 
51
+ def to_type( typecode, options = {} )
52
+ typecode
53
+ end
54
+
55
+ def coercion( other )
56
+ if self == other
57
+ self
58
+ else
59
+ x, y = other.coerce self
60
+ x.coercion y
61
+ end
62
+ end
63
+
64
+ def coerce( other )
65
+ if self == other
66
+ return other, self
67
+ else
68
+ raise "Cannot coerce #{self} and #{other.inspect}"
69
+ end
70
+ end
71
+
47
72
  def ===( other )
48
73
  ( other == self ) or ( other.is_a? self ) or ( other.class == self )
49
74
  end
@@ -134,6 +159,10 @@ module Hornetseye
134
159
  end
135
160
  end
136
161
 
162
+ def to_type( target, options = {} )
163
+ self.class.to_type( target, options ).new.operation( self ) { |x| set x }
164
+ end
165
+
137
166
  def -@
138
167
  if is_a?( Pointer_ ) and self.class.primitive < Sequence_
139
168
  retval = self.class.new
@@ -149,10 +178,11 @@ module Hornetseye
149
178
  end
150
179
 
151
180
  def +( other )
152
- if is_a?( Pointer_ ) and self.class.primitive < Sequence_
153
- retval = self.class.new
181
+ target = self.class.coercion( other.class )
182
+ if target < Pointer_ and target.primitive < Sequence_
183
+ retval = target.new
154
184
  else
155
- retval = self.class.dereference.new
185
+ retval = target.dereference.new
156
186
  end
157
187
  retval.operation( self, other ) { |x,y| set x + y }
158
188
  retval
@@ -94,7 +94,15 @@ class TC_Int < Test::Unit::TestCase
94
94
  end
95
95
  end
96
96
 
97
- def test_lazy
97
+ def test_plus
98
+ T.each do |t1|
99
+ T.each do |t2|
100
+ assert_equal 5, ( t1.new( 3 ) + t2.new( 2 ) )[]
101
+ end
102
+ end
103
+ end
104
+
105
+ def test_lazy_unary
98
106
  T.select { |t| SIGNED[ t ] }.each do |t|
99
107
  i = lazy { -t.new( 3 ) }
100
108
  assert_not_equal t.new( -3 ), i
@@ -115,4 +123,16 @@ class TC_Int < Test::Unit::TestCase
115
123
  end
116
124
  end
117
125
 
126
+ def test_lazy_binary
127
+ a = U16.new 3
128
+ b = S8.new -5
129
+ i = lazy { a + b }
130
+ assert_not_equal a + b, i
131
+ assert_equal 'SINT(<delayed>)', i.inspect
132
+ assert_equal S16.new( -2 ), i.force
133
+ assert_equal S32.new( -1 ), i + S32.new( 1 )
134
+ assert_equal S32.new( -1 ), S32.new( 1 ) + i
135
+ end
136
+
137
+
118
138
  end
@@ -81,12 +81,12 @@ class TC_MultiArray < Test::Unit::TestCase
81
81
  end
82
82
 
83
83
  def test_negate
84
- m = M[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
84
+ m = M[ [ 1, 2, 3 ], [ 4, 5, 6 ] ].to_type O
85
85
  assert_equal [ [ -1, -2, -3 ], [ -4, -5, -6 ] ], ( -m ).to_a
86
86
  end
87
87
 
88
88
  def test_lazy
89
- m = M[ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
89
+ m = M[ [ 1, 2, 3 ], [ 4, 5, 6 ] ].to_type O
90
90
  u = lazy { -m }
91
91
  assert_equal 'MultiArray.object(3,2):<delayed>', u.inspect
92
92
  assert_equal [ [ -1, -2, -3 ], [ -4, -5, -6 ] ], u.force.to_a
@@ -64,7 +64,13 @@ class TC_Object < Test::Unit::TestCase
64
64
  assert_equal O.new( -5 ), -o
65
65
  end
66
66
 
67
- def test_lazy
67
+ def test_plus
68
+ v = O.new 3
69
+ w = O.new 5
70
+ assert_equal O.new( 3 + 5 ), v + w
71
+ end
72
+
73
+ def test_lazy_unary
68
74
  o = lazy { -O.new( 3 ) }
69
75
  assert_not_equal O.new( -3 ), o
70
76
  assert_equal 'OBJECT(<delayed>)', o.inspect
@@ -83,4 +89,15 @@ class TC_Object < Test::Unit::TestCase
83
89
  assert_equal O.new( 3 ), o
84
90
  end
85
91
 
92
+ def test_lazy_binary
93
+ v = O.new 3
94
+ w = O.new 5
95
+ o = lazy { v + w }
96
+ assert_not_equal v + w, o
97
+ assert_equal 'OBJECT(<delayed>)', o.inspect
98
+ assert_equal O.new( 8 ), o.force
99
+ assert_equal O.new( 9 ), o + O.new( 1 )
100
+ assert_equal O.new( 9 ), O.new( 1 ) + o
101
+ end
102
+
86
103
  end
@@ -72,6 +72,7 @@ class TC_Sequence < Test::Unit::TestCase
72
72
 
73
73
  def test_sequence_assign
74
74
  assert_equal [ :a, :b, :c ], S[ :a, :b, :c ].to_a
75
+ assert_equal [ :a, :b, [ :c ] ], S[ :a, :b, [ :c ] ].to_a
75
76
  end
76
77
 
77
78
  def test_inspect
@@ -98,12 +99,19 @@ class TC_Sequence < Test::Unit::TestCase
98
99
  end
99
100
 
100
101
  def test_negate
101
- s = S[ 1, 2, 3 ]
102
+ s = S[ 1, 2, 3 ].to_type O
102
103
  assert_equal [ -1, -2, -3 ], ( -s ).to_a
103
104
  end
104
105
 
105
- def test_lazy
106
- s = S[ 1, 2, 3 ]
106
+ def test_plus
107
+ s = S[ 1, 2, 3 ].to_type O
108
+ assert_equal [ 2, 4, 6 ], ( s + s ).to_a
109
+ assert_equal [ 2, 3, 4 ], ( s + O.new( 1 ) ).to_a
110
+ assert_equal [ 2, 3, 4 ], ( O.new( 1 ) + s ).to_a
111
+ end
112
+
113
+ def test_lazy_unary
114
+ s = S[ 1, 2, 3 ].to_type O
107
115
  u = lazy { -s }
108
116
  assert_equal 'Sequence.object(3):<delayed>', u.inspect
109
117
  assert_equal [ -1, -2, -3 ], u.force.to_a
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: multiarray
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Wedekind
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-26 00:00:00 +00:00
12
+ date: 2010-01-28 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -34,6 +34,7 @@ files:
34
34
  - Rakefile
35
35
  - README
36
36
  - COPYING
37
+ - TODO
37
38
  - .document
38
39
  - lib/multiarray.rb
39
40
  - lib/multiarray/sequence_.rb