multiarray 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -3
- data/TODO +7 -31
- data/lib/multiarray.rb +8 -4
- data/lib/multiarray/complex.rb +360 -0
- data/lib/multiarray/composite.rb +65 -0
- data/lib/multiarray/diagonal.rb +8 -0
- data/lib/multiarray/element.rb +3 -3
- data/lib/multiarray/{unarymethod.rb → elementwise.rb} +66 -44
- data/lib/multiarray/gccfunction.rb +14 -20
- data/lib/multiarray/gcctype.rb +8 -8
- data/lib/multiarray/gccvalue.rb +57 -15
- data/lib/multiarray/inject.rb +10 -2
- data/lib/multiarray/lambda.rb +21 -3
- data/lib/multiarray/lookup.rb +8 -0
- data/lib/multiarray/methods.rb +10 -4
- data/lib/multiarray/node.rb +80 -15
- data/lib/multiarray/operations.rb +193 -12
- data/lib/multiarray/pointer.rb +19 -0
- data/lib/multiarray/rgb.rb +15 -45
- data/lib/multiarray/sequence.rb +34 -5
- data/test/tc_float.rb +20 -41
- data/test/tc_int.rb +10 -0
- data/test/tc_multiarray.rb +225 -49
- data/test/tc_object.rb +11 -0
- data/test/tc_rgb.rb +15 -0
- data/test/tc_sequence.rb +224 -11
- metadata +13 -9
- data/lib/multiarray/binarymethod.rb +0 -195
- data/lib/multiarray/binaryop.rb +0 -189
- data/lib/multiarray/unaryop.rb +0 -179
@@ -0,0 +1,65 @@
|
|
1
|
+
# multiarray - Lazy multi-dimensional arrays for Ruby
|
2
|
+
# Copyright (C) 2010 Jan Wedekind
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
# Namespace of Hornetseye computer vision library
|
18
|
+
module Hornetseye
|
19
|
+
|
20
|
+
class Composite < Element
|
21
|
+
|
22
|
+
class << self
|
23
|
+
|
24
|
+
attr_accessor :element_type
|
25
|
+
|
26
|
+
attr_accessor :num_elements
|
27
|
+
|
28
|
+
def memory
|
29
|
+
element_type.memory
|
30
|
+
end
|
31
|
+
|
32
|
+
def storage_size
|
33
|
+
element_type.storage_size * num_elements
|
34
|
+
end
|
35
|
+
|
36
|
+
def directive
|
37
|
+
element_type.directive * num_elements
|
38
|
+
end
|
39
|
+
|
40
|
+
def descriptor( hash )
|
41
|
+
unless element_type.nil?
|
42
|
+
inspect
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def basetype
|
49
|
+
element_type
|
50
|
+
end
|
51
|
+
|
52
|
+
def typecodes
|
53
|
+
[ element_type ] * num_elements
|
54
|
+
end
|
55
|
+
|
56
|
+
def scalar
|
57
|
+
element_type
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
data/lib/multiarray/diagonal.rb
CHANGED
data/lib/multiarray/element.rb
CHANGED
@@ -56,7 +56,7 @@ module Hornetseye
|
|
56
56
|
#
|
57
57
|
# @param [Object] value Initial value for element.
|
58
58
|
def initialize( value = self.class.default )
|
59
|
-
if Thread.current[ :function ].nil?
|
59
|
+
if Thread.current[ :function ].nil?
|
60
60
|
@value = value
|
61
61
|
else
|
62
62
|
@value = GCCValue.new Thread.current[ :function ], value.to_s
|
@@ -84,8 +84,8 @@ module Hornetseye
|
|
84
84
|
def dup
|
85
85
|
if Thread.current[ :function ]
|
86
86
|
value = Thread.current[ :function ].variable self.class, 'v'
|
87
|
-
value.
|
88
|
-
value
|
87
|
+
value.store get
|
88
|
+
self.class.new value
|
89
89
|
else
|
90
90
|
self.class.new get
|
91
91
|
end
|
@@ -17,31 +17,30 @@
|
|
17
17
|
# Namespace of Hornetseye computer vision library
|
18
18
|
module Hornetseye
|
19
19
|
|
20
|
-
|
21
|
-
class UnaryMethod_ < Node
|
20
|
+
class ElementWise_ < Node
|
22
21
|
|
23
22
|
class << self
|
24
23
|
|
25
|
-
# Name of module
|
26
|
-
#
|
27
|
-
# @return [Module] The module with the method.
|
28
|
-
attr_accessor :mod
|
29
|
-
|
30
24
|
# Name of operation
|
31
25
|
#
|
32
|
-
# @return [
|
26
|
+
# @return [Proc] A closure with the operation.
|
33
27
|
attr_accessor :operation
|
34
28
|
|
29
|
+
# Unique key to identify operation.
|
30
|
+
#
|
31
|
+
# @return [Symbol,String] A unique key to identify this operation.
|
32
|
+
attr_accessor :key
|
33
|
+
|
35
34
|
# Name of method for type conversion
|
36
35
|
#
|
37
|
-
# @return [
|
36
|
+
# @return [Proc] A closure for doing the type conversion.
|
38
37
|
attr_accessor :conversion
|
39
38
|
|
40
39
|
# Get string with information about this class
|
41
40
|
#
|
42
41
|
# @return [String] Return string with information about this class.
|
43
42
|
def inspect
|
44
|
-
|
43
|
+
key.to_s
|
45
44
|
end
|
46
45
|
|
47
46
|
# Get unique descriptor of this class
|
@@ -55,13 +54,18 @@ module Hornetseye
|
|
55
54
|
inspect
|
56
55
|
end
|
57
56
|
|
57
|
+
def finalised?
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
58
61
|
end
|
59
62
|
|
60
63
|
# Initialise unary operation
|
61
64
|
#
|
62
65
|
# @param [Node] value Value to apply operation to.
|
63
|
-
def initialize(
|
64
|
-
@
|
66
|
+
def initialize( *values )
|
67
|
+
@values = values
|
68
|
+
check_shape *values
|
65
69
|
end
|
66
70
|
|
67
71
|
# Get unique descriptor of this object
|
@@ -72,7 +76,8 @@ module Hornetseye
|
|
72
76
|
#
|
73
77
|
# @private
|
74
78
|
def descriptor( hash )
|
75
|
-
"#{self.class.descriptor( hash )}
|
79
|
+
"#{self.class.descriptor( hash )}" +
|
80
|
+
"(#{@values.collect { |value| value.descriptor( hash ) }.join ','})"
|
76
81
|
end
|
77
82
|
|
78
83
|
# Array type of this term
|
@@ -81,7 +86,19 @@ module Hornetseye
|
|
81
86
|
#
|
82
87
|
# @private
|
83
88
|
def array_type
|
84
|
-
@value.array_type
|
89
|
+
array_types = @values.collect { |value| value.array_type }
|
90
|
+
self.class.conversion.call *array_types
|
91
|
+
end
|
92
|
+
|
93
|
+
# Reevaluate computation
|
94
|
+
#
|
95
|
+
# @return [Node,Object] Result of computation
|
96
|
+
#
|
97
|
+
# @see #force
|
98
|
+
#
|
99
|
+
# @private
|
100
|
+
def demand
|
101
|
+
self.class.operation.call *@values
|
85
102
|
end
|
86
103
|
|
87
104
|
# Substitute variables
|
@@ -94,7 +111,7 @@ module Hornetseye
|
|
94
111
|
#
|
95
112
|
# @private
|
96
113
|
def subst( hash )
|
97
|
-
self.class.new
|
114
|
+
self.class.new *@values.collect { |value| value.subst( hash ) }
|
98
115
|
end
|
99
116
|
|
100
117
|
# Get variables contained in this term
|
@@ -103,7 +120,7 @@ module Hornetseye
|
|
103
120
|
#
|
104
121
|
# @private
|
105
122
|
def variables
|
106
|
-
@value.variables
|
123
|
+
@values.inject( Set[] ) { |vars,value| vars + value.variables }
|
107
124
|
end
|
108
125
|
|
109
126
|
# Strip of all values
|
@@ -116,23 +133,15 @@ module Hornetseye
|
|
116
133
|
#
|
117
134
|
# @private
|
118
135
|
def strip
|
119
|
-
|
120
|
-
return vars,
|
121
|
-
|
122
|
-
|
123
|
-
# Reevaluate computation
|
124
|
-
#
|
125
|
-
# @return [Node,Object] Result of computation
|
126
|
-
#
|
127
|
-
# @see #force
|
128
|
-
#
|
129
|
-
# @private
|
130
|
-
def demand
|
131
|
-
self.class.mod.send self.class.operation, @value
|
136
|
+
stripped = @values.collect { |value| value.strip }
|
137
|
+
return stripped.inject( [] ) { |vars,elem| vars + elem[ 0 ] },
|
138
|
+
stripped.inject( [] ) { |values,elem| values + elem[ 1 ] },
|
139
|
+
self.class.new( *stripped.collect { |elem| elem[ 2 ] } )
|
132
140
|
end
|
133
141
|
|
134
142
|
def skip( index, start )
|
135
|
-
|
143
|
+
skipped = *@values.collect { |value| value.skip( index, start ) }
|
144
|
+
self.class.new( *skipped ).demand
|
136
145
|
end
|
137
146
|
|
138
147
|
# Get element of unary operation
|
@@ -141,11 +150,22 @@ module Hornetseye
|
|
141
150
|
#
|
142
151
|
# @return [Node,Object] Element of unary operation.
|
143
152
|
def element( i )
|
144
|
-
|
153
|
+
values = @values.collect do |value|
|
154
|
+
value.dimension == 0 ? value : value.element( i )
|
155
|
+
end
|
156
|
+
self.class.new( *values ).demand
|
145
157
|
end
|
146
158
|
|
147
159
|
def slice( start, length )
|
148
|
-
|
160
|
+
values = @values.collect do |value|
|
161
|
+
value.dimension == 0 ? value : value.slice( start, length )
|
162
|
+
end
|
163
|
+
self.class.new( *values ).demand
|
164
|
+
end
|
165
|
+
|
166
|
+
def decompose
|
167
|
+
values = @values.collect { |value| value.decompose }
|
168
|
+
self.class.new( *values ).demand
|
149
169
|
end
|
150
170
|
|
151
171
|
# Check whether this term is compilable
|
@@ -154,32 +174,34 @@ module Hornetseye
|
|
154
174
|
#
|
155
175
|
# @private
|
156
176
|
def compilable?
|
157
|
-
@value.compilable?
|
177
|
+
array_type.compilable? and @values.all? { |value| value.compilable? }
|
158
178
|
end
|
159
179
|
|
160
180
|
end
|
161
181
|
|
162
|
-
# Create a class deriving from +
|
182
|
+
# Create a class deriving from +ElementWise_+
|
163
183
|
#
|
164
|
-
# @param [
|
165
|
-
# @param [Symbol,String]
|
184
|
+
# @param [Proc] operation A closure with the operation to perform.
|
185
|
+
# @param [Symbol,String] key A unique descriptor to identify this operation.
|
186
|
+
# @param [Proc] conversion A closure for performing the type conversion.
|
166
187
|
#
|
167
|
-
# @return [Class] A class deriving from +
|
188
|
+
# @return [Class] A class deriving from +ElementWise_+.
|
168
189
|
#
|
169
|
-
# @see
|
170
|
-
# @see
|
171
|
-
# @see
|
190
|
+
# @see ElementWise_
|
191
|
+
# @see ElementWise_.operation
|
192
|
+
# @see ElementWise_.key
|
193
|
+
# @see ElementWise_.conversion
|
172
194
|
#
|
173
195
|
# @private
|
174
|
-
def
|
175
|
-
retval = Class.new
|
176
|
-
retval.mod = mod
|
196
|
+
def ElementWise( operation, key, conversion = lambda { |t| t.send :contiguous } )
|
197
|
+
retval = Class.new ElementWise_
|
177
198
|
retval.operation = operation
|
199
|
+
retval.key = key
|
178
200
|
retval.conversion = conversion
|
179
201
|
retval
|
180
202
|
end
|
181
203
|
|
182
|
-
module_function :
|
204
|
+
module_function :ElementWise
|
183
205
|
|
184
206
|
end
|
185
207
|
|
@@ -21,25 +21,29 @@ module Hornetseye
|
|
21
21
|
|
22
22
|
class << self
|
23
23
|
|
24
|
-
def run( block )
|
24
|
+
def run( retval, block )
|
25
25
|
keys, values, term = block.strip
|
26
|
-
labels = Hash[ *keys.
|
27
|
-
|
26
|
+
labels = Hash[ *keys.
|
27
|
+
zip( ( 0 ... keys.size ).to_a ).flatten ]
|
28
28
|
retval_keys, retval_values, retval_term = retval.strip
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
retval_labels = Hash[ *retval_keys.
|
30
|
+
zip( ( 0 ... retval_keys.size ).to_a ).flatten ]
|
31
|
+
method_name = ( '_' + retval_term.descriptor( retval_labels ) +
|
32
|
+
'_' + term.descriptor( labels ) ).
|
33
|
+
tr( '(),+\-*/%.@?~&|^<=>',
|
34
|
+
'0123\456789ABCDEFGH' )
|
32
35
|
unless GCCCache.respond_to? method_name
|
33
36
|
GCCContext.build do |context|
|
34
37
|
function = new context, method_name,
|
35
38
|
*( retval_keys + keys ).collect { |var| var.meta }
|
39
|
+
Thread.current[ :function ] = function
|
36
40
|
term_subst = ( 0 ... keys.size ).collect do |i|
|
37
41
|
{ keys[i] => function.param( i + retval_keys.size ) }
|
38
42
|
end.inject( {} ) { |a,b| a.merge b }
|
39
43
|
retval_subst = ( 0 ... retval_keys.size ).collect do |i|
|
40
44
|
{ retval_keys[ i ] => function.param( i ) }
|
41
45
|
end.inject( {} ) { |a,b| a.merge b }
|
42
|
-
Thread.current[ :function ] = function
|
46
|
+
# Thread.current[ :function ] = function
|
43
47
|
Hornetseye::lazy do
|
44
48
|
retval_term.subst( retval_subst ).store term.subst( term_subst )
|
45
49
|
end
|
@@ -76,19 +80,9 @@ module Hornetseye
|
|
76
80
|
end
|
77
81
|
|
78
82
|
def variable( typecode, prefix )
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
# b = GCCValue.new( self, id( prefix ) )
|
83
|
-
# self << "#{indent}#{GCCType.new( INT ).identifier} #{r};\n"
|
84
|
-
# self << "#{indent}#{GCCType.new( INT ).identifier} #{g};\n"
|
85
|
-
# self << "#{indent}#{GCCType.new( INT ).identifier} #{b};\n"
|
86
|
-
# INTRGB.new RGB.new( r, g, b )
|
87
|
-
#else
|
88
|
-
retval = typecode.new GCCValue.new( self, id( prefix ) )
|
89
|
-
self << "#{indent}#{GCCType.new( typecode ).identifiers.first} #{retval.get};\n" # !!!
|
90
|
-
retval
|
91
|
-
#end
|
83
|
+
retval = GCCValue.new( self, id( prefix ) )
|
84
|
+
self << "#{indent}#{GCCType.new( typecode ).identifiers.first} #{retval};\n" # !!!
|
85
|
+
retval
|
92
86
|
end
|
93
87
|
|
94
88
|
def indent
|
data/lib/multiarray/gcctype.rb
CHANGED
@@ -50,8 +50,8 @@ module Hornetseye
|
|
50
50
|
[ 'void *' ]
|
51
51
|
elsif @typecode < INDEX_
|
52
52
|
[ 'int' ]
|
53
|
-
elsif @typecode <
|
54
|
-
GCCType.new( @typecode.element_type ).identifiers *
|
53
|
+
elsif @typecode < Composite
|
54
|
+
GCCType.new( @typecode.element_type ).identifiers * @typecode.num_elements
|
55
55
|
else
|
56
56
|
raise "No identifier available for #{@typecode.inspect}"
|
57
57
|
end
|
@@ -61,16 +61,16 @@ module Hornetseye
|
|
61
61
|
def r2c
|
62
62
|
case @typecode
|
63
63
|
when BOOL
|
64
|
-
[
|
64
|
+
[ lambda { |expr| "( #{expr} ) != Qfalse" } ]
|
65
65
|
when BYTE, UBYTE, SINT, USINT, INT, UINT
|
66
|
-
[
|
66
|
+
[ lambda { |expr| "NUM2INT( #{expr} )" } ]
|
67
67
|
when SFLOAT, DFLOAT
|
68
|
-
[
|
68
|
+
[ lambda { |expr| "NUM2DBL( #{expr} )" } ]
|
69
69
|
else
|
70
70
|
if @typecode < Pointer_
|
71
|
-
[
|
72
|
-
elsif @typecode <
|
73
|
-
GCCType.new( @typecode.element_type ).r2c *
|
71
|
+
[ lambda { |expr| "(#{identifiers.first})mallocToPtr( #{expr} )" } ] # !!!
|
72
|
+
elsif @typecode < Composite
|
73
|
+
GCCType.new( @typecode.element_type ).r2c * @typecode.num_elements
|
74
74
|
else
|
75
75
|
raise "No conversion available for #{@typecode.inspect}"
|
76
76
|
end
|
data/lib/multiarray/gccvalue.rb
CHANGED
@@ -62,7 +62,8 @@ module Hornetseye
|
|
62
62
|
mod.module_eval do
|
63
63
|
define_method( "#{op}_with_gcc" ) do |a,b|
|
64
64
|
if a.is_a? GCCValue or b.is_a? GCCValue
|
65
|
-
|
65
|
+
function = a.is_a?( GCCValue ) ? a.function : b.function
|
66
|
+
GCCValue.new function, "#{opcode}( #{a}, #{b} )"
|
66
67
|
else
|
67
68
|
send "#{op}_without_gcc", a, b
|
68
69
|
end
|
@@ -117,6 +118,18 @@ module Hornetseye
|
|
117
118
|
end
|
118
119
|
end
|
119
120
|
|
121
|
+
def conj
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
def abs
|
126
|
+
( self >= 0 ).conditional self, -self
|
127
|
+
end
|
128
|
+
|
129
|
+
def arg
|
130
|
+
( self >= 0 ).conditional 0, Math::PI
|
131
|
+
end
|
132
|
+
|
120
133
|
def r
|
121
134
|
self
|
122
135
|
end
|
@@ -129,6 +142,14 @@ module Hornetseye
|
|
129
142
|
self
|
130
143
|
end
|
131
144
|
|
145
|
+
def real
|
146
|
+
self
|
147
|
+
end
|
148
|
+
|
149
|
+
def imag
|
150
|
+
0
|
151
|
+
end
|
152
|
+
|
132
153
|
def conditional( a, b )
|
133
154
|
GCCValue.new @function, "( #{self} ) ? ( #{a} ) : ( #{b} )"
|
134
155
|
end
|
@@ -147,6 +168,7 @@ module Hornetseye
|
|
147
168
|
define_binary_op :-
|
148
169
|
define_binary_op :*
|
149
170
|
define_binary_op :/
|
171
|
+
define_binary_op :%
|
150
172
|
define_binary_op :eq, :==
|
151
173
|
define_binary_op :ne, '!='
|
152
174
|
define_binary_op :<
|
@@ -168,6 +190,34 @@ module Hornetseye
|
|
168
190
|
define_binary_method Math, :atan2
|
169
191
|
define_binary_method Math, :hypot
|
170
192
|
|
193
|
+
def zero?
|
194
|
+
GCCValue.new @function, "( #{self} ) == 0"
|
195
|
+
end
|
196
|
+
|
197
|
+
def nonzero?
|
198
|
+
GCCValue.new @function, "( #{self} ) != 0"
|
199
|
+
end
|
200
|
+
|
201
|
+
def floor
|
202
|
+
GCCValue.new @function, "floor( #{self} )"
|
203
|
+
end
|
204
|
+
|
205
|
+
def ceil
|
206
|
+
GCCValue.new @function, "ceil( #{self} )"
|
207
|
+
end
|
208
|
+
|
209
|
+
def round
|
210
|
+
GCCValue.new @function, "round( #{self} )"
|
211
|
+
end
|
212
|
+
|
213
|
+
def **( other )
|
214
|
+
if GCCValue.generic? other
|
215
|
+
GCCValue.new @function, "pow( #{self}, #{other} )"
|
216
|
+
else
|
217
|
+
x, y = other.coerce self
|
218
|
+
x ** y
|
219
|
+
end
|
220
|
+
end
|
171
221
|
|
172
222
|
def major( other )
|
173
223
|
GCCValue.new @function,
|
@@ -179,20 +229,12 @@ module Hornetseye
|
|
179
229
|
"( ( #{self} ) <= ( #{other} ) ) ? ( #{self} ) : ( #{other} )"
|
180
230
|
end
|
181
231
|
|
182
|
-
def zero?
|
183
|
-
GCCValue.new @function, "( #{self} ) == 0"
|
184
|
-
end
|
185
|
-
|
186
|
-
def nonzero?
|
187
|
-
GCCValue.new @function, "( #{self} ) != 0"
|
188
|
-
end
|
189
|
-
|
190
232
|
def times( &action )
|
191
233
|
i = @function.variable INT, 'i'
|
192
|
-
@function << "#{@function.indent}for ( #{i
|
193
|
-
"#{i
|
234
|
+
@function << "#{@function.indent}for ( #{i} = 0; " +
|
235
|
+
"#{i} != #{self}; #{i}++ ) {\n"
|
194
236
|
@function.indent_offset +1
|
195
|
-
action.call i
|
237
|
+
action.call i
|
196
238
|
@function.indent_offset -1
|
197
239
|
@function << "#{@function.indent}};\n"
|
198
240
|
self
|
@@ -200,10 +242,10 @@ module Hornetseye
|
|
200
242
|
|
201
243
|
def upto( other, &action )
|
202
244
|
i = @function.variable INT, 'i'
|
203
|
-
@function << "#{@function.indent}for ( #{i
|
204
|
-
"#{i
|
245
|
+
@function << "#{@function.indent}for ( #{i} = #{self}; " +
|
246
|
+
"#{i} != #{ other + 1 }; #{i}++ ) {\n"
|
205
247
|
@function.indent_offset +1
|
206
|
-
action.call i
|
248
|
+
action.call i
|
207
249
|
@function.indent_offset -1
|
208
250
|
@function << "#{@function.indent}};\n"
|
209
251
|
self
|