multiarray 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -22,13 +22,6 @@ module Hornetseye
22
22
 
23
23
  class << self
24
24
 
25
- # Get string with information about this class
26
- #
27
- # @return [String] Returns +'Node'+.
28
- def inspect
29
- 'Node'
30
- end
31
-
32
25
  # Get unique descriptor of this class
33
26
  #
34
27
  # The method calls +descriptor( {} )+.
@@ -62,21 +55,10 @@ module Hornetseye
62
55
  # @private
63
56
  def match( value, context = nil )
64
57
  retval = fit value
65
- retval = retval.align context if context
58
+ retval = retval.align context.basetype if context
66
59
  retval
67
60
  end
68
61
 
69
- # Align this native datatype with another
70
- #
71
- # @param [Class] Native datatype to align with.
72
- #
73
- # @return [Class] Aligned native datatype.
74
- #
75
- # @private
76
- def align( context )
77
- self
78
- end
79
-
80
62
  # Element-type of this term
81
63
  #
82
64
  # @return [Class] Element-type of this datatype.
@@ -84,6 +66,14 @@ module Hornetseye
84
66
  self
85
67
  end
86
68
 
69
+ def basetype
70
+ self
71
+ end
72
+
73
+ def typecodes
74
+ [ self ]
75
+ end
76
+
87
77
  # Array type of this term
88
78
  #
89
79
  # @return [Class] Resulting array type.
@@ -100,6 +90,10 @@ module Hornetseye
100
90
  Hornetseye::Pointer( self )
101
91
  end
102
92
 
93
+ def indgen( offset = 0, increment = 1 )
94
+ offset
95
+ end
96
+
103
97
  # Get shape of this term
104
98
  #
105
99
  # @return [Array<Integer>] Returns +[]+.
@@ -107,6 +101,17 @@ module Hornetseye
107
101
  []
108
102
  end
109
103
 
104
+ # Get size (number of elements) of this value
105
+ #
106
+ # @return [Integer] Returns +1+.
107
+ def size
108
+ 1
109
+ end
110
+
111
+ def empty?
112
+ size == 0
113
+ end
114
+
110
115
  # Get dimension of this term
111
116
  #
112
117
  # @return [Array<Integer>] Returns +0+.
@@ -137,6 +142,25 @@ module Hornetseye
137
142
  other.coercion( self ).bool
138
143
  end
139
144
 
145
+ def maxint
146
+ self
147
+ end
148
+
149
+ def largeint( other )
150
+ coercion( other ).maxint
151
+ end
152
+
153
+ # Get corresponding floating-point datatype
154
+ #
155
+ # @return [Class] Returns +DFLOAT+.
156
+ def float
157
+ DFLOAT
158
+ end
159
+
160
+ def floating( other )
161
+ other.coercion( self ).float
162
+ end
163
+
140
164
  # Get variables contained in this datatype
141
165
  #
142
166
  # @return [Set] Returns +Set[]+.
@@ -218,6 +242,17 @@ module Hornetseye
218
242
  array_type.shape
219
243
  end
220
244
 
245
+ # Get size (number of elements) of this value
246
+ #
247
+ # @return [Integer] Returns +array_type.size+.
248
+ def size
249
+ array_type.size
250
+ end
251
+
252
+ def empty?
253
+ array_type.empty?
254
+ end
255
+
221
256
  # Get dimension of this term
222
257
  #
223
258
  # @return [Array<Integer>] Returns +array_type.dimension+.
@@ -257,42 +292,50 @@ module Hornetseye
257
292
  if dimension == 0 and not indent
258
293
  "#{array_type.inspect}(#{force.inspect})" # !!!
259
294
  else
260
- prepend = indent ? '' : "#{array_type.inspect}:\n"
261
- indent = 0
262
- lines = 0
263
- retval = '[ '
264
- for i in 0 ... array_type.num_elements
265
- x = Hornetseye::lazy { element i }
266
- if x.dimension > 0
267
- if i > 0
268
- retval += ",\n "
269
- lines += 1
295
+ if indent
296
+ prepend = ''
297
+ else
298
+ prepend = "#{array_type.inspect}:\n"
299
+ indent = 0
300
+ lines = 0
301
+ end
302
+ if empty?
303
+ retval = '[]'
304
+ else
305
+ retval = '[ '
306
+ for i in 0 ... array_type.num_elements
307
+ x = Hornetseye::lazy { element i }
308
+ if x.dimension > 0
309
+ if i > 0
310
+ retval += ",\n "
311
+ lines += 1
312
+ if lines >= 10
313
+ retval += '...' if indent == 0
314
+ break
315
+ end
316
+ retval += ' ' * indent
317
+ end
318
+ str = x.inspect indent + 1, lines
319
+ lines += str.count "\n"
320
+ retval += str
270
321
  if lines >= 10
271
322
  retval += '...' if indent == 0
272
323
  break
273
324
  end
274
- retval += ' ' * indent
275
- end
276
- str = x.inspect indent + 1, lines
277
- lines += str.count "\n"
278
- retval += str
279
- if lines >= 10
280
- retval += '...' if indent == 0
281
- break
282
- end
283
- else
284
- retval += ', ' if i > 0
285
- str = x.force.inspect # !!!
286
- if retval.size + str.size >= 74 - '...'.size -
287
- '[ ]'.size * indent.succ
288
- retval += '...'
289
- break
290
325
  else
291
- retval += str
326
+ retval += ', ' if i > 0
327
+ str = x.force.inspect # !!!
328
+ if retval.size + str.size >= 74 - '...'.size -
329
+ '[ ]'.size * indent.succ
330
+ retval += '...'
331
+ break
332
+ else
333
+ retval += str
334
+ end
292
335
  end
293
336
  end
337
+ retval += ' ]' unless lines >= 10
294
338
  end
295
- retval += ' ]' unless lines >= 10
296
339
  prepend + retval
297
340
  end
298
341
  else
@@ -346,25 +389,6 @@ module Hornetseye
346
389
  typecode.compilable?
347
390
  end
348
391
 
349
- # Lazy transpose of array
350
- #
351
- # Lazily compute transpose by swapping indices according to the specified
352
- # order.
353
- #
354
- # @param [Array<Integer>] order New order of indices.
355
- #
356
- # @return [Node] Returns the transposed array.
357
- def transpose( *order )
358
- term = self
359
- variables = shape.reverse.collect do |i|
360
- var = Variable.new INDEX( i )
361
- term = term.element var
362
- var
363
- end.reverse
364
- order.collect { |o| variables[o] }.
365
- inject( term ) { |retval,var| Lambda.new var, retval }
366
- end
367
-
368
392
  # Retrieve value of array element(s)
369
393
  #
370
394
  # @param [Array<Integer>] *indices Index/indices to select element.
@@ -374,7 +398,12 @@ module Hornetseye
374
398
  if indices.empty?
375
399
  force
376
400
  else
377
- element( indices.last )[ *indices[ 0 ... -1 ] ]
401
+ if indices.last.is_a? Range
402
+ view = slice indices.last.min, indices.last.size
403
+ else
404
+ view = element indices.last
405
+ end
406
+ view[ *indices[ 0 ... -1 ] ]
378
407
  end
379
408
  end
380
409
 
@@ -392,7 +421,12 @@ module Hornetseye
392
421
  if indices.empty?
393
422
  store value
394
423
  else
395
- element( indices.last )[ *indices[ 0 ... -1 ] ] = value
424
+ if indices.last.is_a? Range
425
+ view = slice indices.last.min, indices.last.size
426
+ else
427
+ view = element indices.last
428
+ end
429
+ view[ *indices[ 0 ... -1 ] ] = value
396
430
  end
397
431
  end
398
432
 
@@ -465,7 +499,7 @@ module Hornetseye
465
499
  #
466
500
  # @return [Node,Object] Result of simplification
467
501
  #
468
- # @See @demand
502
+ # @see demand
469
503
  #
470
504
  # @private
471
505
  def simplify
@@ -487,110 +521,6 @@ module Hornetseye
487
521
  end
488
522
  end
489
523
 
490
- def inject( initial = nil, options = {} )
491
- unless initial.nil?
492
- initial = Node.match( initial ).new initial unless initial.is_a? Node
493
- initial_typecode = initial.typecode
494
- else
495
- initial_typecode = typecode
496
- end
497
- var1 = options[ :var1 ] || Variable.new( initial_typecode )
498
- var2 = options[ :var2 ] || Variable.new( typecode )
499
- block = options[ :block ] || yield( var1, var2 )
500
- if dimension == 0
501
- if initial
502
- block.subst( var1 => initial, var2 => self ).simplify
503
- else
504
- demand
505
- end
506
- else
507
- index = Variable.new Hornetseye::INDEX( nil )
508
- value = element( index ).
509
- inject nil, :block => block, :var1 => var1, :var2 => var2
510
- Inject.new( value, index, initial, block, var1, var2 ).force
511
- end
512
- end
513
-
514
- # Equality operator
515
- #
516
- # @return [FalseClass,TrueClass] Returns result of comparison.
517
- def ==( other )
518
- if other.is_a? Node and other.array_type == array_type
519
- Hornetseye::eager { eq( other ).inject( true ) { |a,b| a.and b } }
520
- else
521
- false
522
- end
523
- end
524
-
525
- # Apply accumulative operation over elements diagonally
526
- #
527
- # This method is used internally to implement convolutions.
528
- #
529
- # @param [Object,Node] initial Initial value.
530
- # @option options [Variable] :var1 First variable defining operation.
531
- # @option options [Variable] :var2 Second variable defining operation.
532
- # @option options [Variable] :block (yield( var1, var2 )) The operation to
533
- # apply diagonally.
534
- # @yield Optional operation to apply diagonally.
535
- #
536
- # @return [Node] Result of operation.
537
- #
538
- # @see #convolve
539
- #
540
- # @private
541
- def diagonal( initial = nil, options = {} )
542
- if dimension == 0
543
- demand
544
- else
545
- if initial
546
- initial = Node.match( initial ).new initial unless initial.is_a? Node
547
- initial_typecode = initial.typecode
548
- else
549
- initial_typecode = typecode
550
- end
551
- index0 = Variable.new Hornetseye::INDEX( nil )
552
- index1 = Variable.new Hornetseye::INDEX( nil )
553
- index2 = Variable.new Hornetseye::INDEX( nil )
554
- var1 = options[ :var1 ] || Variable.new( initial_typecode )
555
- var2 = options[ :var2 ] || Variable.new( typecode )
556
- block = options[ :block ] || yield( var1, var2 )
557
- value = element( index1 ).element( index2 ).
558
- diagonal initial, :block => block, :var1 => var1, :var2 => var2
559
- term = Diagonal.new( value, index0, index1, index2, initial,
560
- block, var1, var2 )
561
- index0.size[] ||= index1.size[]
562
- Lambda.new( index0, term ).force
563
- end
564
- end
565
-
566
- # Compute product table from two arrays
567
- #
568
- # Used internally to implement convolutions.
569
- #
570
- # @param [Node] filter Filter to form product table with.
571
- #
572
- # @return [Node] Result of operation.
573
- #
574
- # @see #convolve
575
- #
576
- # @private
577
- def product( filter )
578
- if dimension == 0
579
- self * filter
580
- else
581
- Hornetseye::lazy { |i,j| self[j].product filter[i] }
582
- end
583
- end
584
-
585
- # Convolution with other array of same dimension
586
- #
587
- # @param [Node] filter Filter to convolve with.
588
- #
589
- # @return [Node] Result of convolution.
590
- def convolve( filter )
591
- product( filter ).diagonal { |s,x| s + x }
592
- end
593
-
594
524
  end
595
525
 
596
526
  end
@@ -37,7 +37,7 @@ module Hornetseye
37
37
  #
38
38
  # @private
39
39
  def descriptor( hash )
40
- 'OBJECT'
40
+ inspect
41
41
  end
42
42
 
43
43
  # Get memory type required to store elements of this type
@@ -86,6 +86,14 @@ module Hornetseye
86
86
  return self, self
87
87
  end
88
88
 
89
+ def bool
90
+ self
91
+ end
92
+
93
+ def float
94
+ OBJECT
95
+ end
96
+
89
97
  # Check whether this term is compilable
90
98
  #
91
99
  # @return [FalseClass,TrueClass] Returns +false+.
@@ -118,10 +126,20 @@ module Hornetseye
118
126
  OBJECT
119
127
  end
120
128
 
129
+ def align( context )
130
+ self
131
+ end
132
+
121
133
  end
122
134
 
123
135
  Node.extend Match
124
136
 
125
137
  end
126
138
 
139
+ def OBJECT( value )
140
+ OBJECT.new value
141
+ end
142
+
143
+ module_function :OBJECT
144
+
127
145
  end
@@ -25,7 +25,7 @@ module Hornetseye
25
25
  target = typecode.send conversion
26
26
  target.new simplify.get.send( op )
27
27
  else
28
- Hornetseye::Unary( op, conversion ).new( self ).force
28
+ Hornetseye::UnaryOp( op, conversion ).new( self ).force
29
29
  end
30
30
  end
31
31
  end
@@ -34,27 +34,33 @@ module Hornetseye
34
34
 
35
35
  def define_binary_op( op, coercion = :coercion )
36
36
  define_method( op ) do |other|
37
- other = Node.match( other, typecode ).new other unless other.is_a? Node
37
+ unless other.is_a? Node
38
+ other = Node.match( other, typecode ).new other
39
+ end
38
40
  if dimension == 0 and variables.empty? and
39
41
  other.dimension == 0 and other.variables.empty?
40
42
  target = array_type.send coercion, other.array_type
41
- target.new demand.get.send( op, other.simplify.get )
43
+ target.new simplify.get.send( op, other.simplify.get )
42
44
  else
43
- Hornetseye::Binary( op, coercion ).new( self, other ).force
45
+ Hornetseye::BinaryOp( op, coercion ).new( self, other ).force
44
46
  end
45
47
  end
46
48
  end
47
49
 
48
50
  module_function :define_binary_op
49
51
 
50
- define_unary_op :zero? , :bool
51
- define_unary_op :nonzero?, :bool
52
- define_unary_op :not, :bool
53
- define_unary_op :~
54
- define_unary_op :-@
52
+ define_unary_op :zero?, :bool
53
+ define_unary_op :nonzero?, :bool
54
+ define_unary_op :not, :bool
55
+ define_unary_op :~
56
+ define_unary_op :-@
57
+ define_unary_op :floor
58
+ define_unary_op :ceil
59
+ define_unary_op :round
55
60
  define_binary_op :+
56
61
  define_binary_op :-
57
62
  define_binary_op :*
63
+ define_binary_op :**, :largeint
58
64
  define_binary_op :/
59
65
  define_binary_op :%
60
66
  define_binary_op :and, :bool_binary
@@ -66,6 +72,180 @@ module Hornetseye
66
72
  define_binary_op :>>
67
73
  define_binary_op :eq, :bool_binary
68
74
  define_binary_op :ne, :bool_binary
75
+ define_binary_op :<=, :bool_binary
76
+ define_binary_op :<, :bool_binary
77
+ define_binary_op :>=, :bool_binary
78
+ define_binary_op :>, :bool_binary
79
+ define_binary_op :<=>, :byteval
80
+ define_binary_op :minor
81
+ define_binary_op :major
82
+
83
+ def +@
84
+ self
85
+ end
86
+
87
+ # Lazy transpose of array
88
+ #
89
+ # Lazily compute transpose by swapping indices according to the specified
90
+ # order.
91
+ #
92
+ # @param [Array<Integer>] order New order of indices.
93
+ #
94
+ # @return [Node] Returns the transposed array.
95
+ def transpose( *order )
96
+ term = self
97
+ variables = shape.reverse.collect do |i|
98
+ var = Variable.new Hornetseye::INDEX( i )
99
+ term = term.element var
100
+ var
101
+ end.reverse
102
+ order.collect { |o| variables[o] }.
103
+ inject( term ) { |retval,var| Lambda.new var, retval }
104
+ end
105
+
106
+ def roll( n = 1 )
107
+ if n < 0
108
+ unroll -n
109
+ else
110
+ order = ( 0 ... dimension ).to_a
111
+ n.times { order = order[ 1 .. -1 ] + [ order.first ] }
112
+ transpose *order
113
+ end
114
+ end
115
+
116
+ def unroll( n = 1 )
117
+ if n < 0
118
+ roll -n
119
+ else
120
+ order = ( 0 ... dimension ).to_a
121
+ n.times { order = [ order.last ] + order[ 0 ... -1 ] }
122
+ transpose *order
123
+ end
124
+ end
125
+
126
+ def inject( initial = nil, options = {} )
127
+ unless initial.nil?
128
+ initial = Node.match( initial ).new initial unless initial.is_a? Node
129
+ initial_typecode = initial.typecode
130
+ else
131
+ initial_typecode = typecode
132
+ end
133
+ var1 = options[ :var1 ] || Variable.new( initial_typecode )
134
+ var2 = options[ :var2 ] || Variable.new( typecode )
135
+ block = options[ :block ] || yield( var1, var2 )
136
+ if dimension == 0
137
+ if initial
138
+ block.subst( var1 => initial, var2 => self ).simplify
139
+ else
140
+ demand
141
+ end
142
+ else
143
+ index = Variable.new Hornetseye::INDEX( nil )
144
+ value = element( index ).
145
+ inject nil, :block => block, :var1 => var1, :var2 => var2
146
+ Inject.new( value, index, initial, block, var1, var2 ).force
147
+ end
148
+ end
149
+
150
+ # Equality operator
151
+ #
152
+ # @return [FalseClass,TrueClass] Returns result of comparison.
153
+ def eq_with_multiarray( other )
154
+ if other.is_a? Node
155
+ if variables.empty?
156
+ if other.array_type == array_type
157
+ Hornetseye::eager { eq( other ).inject( true ) { |a,b| a.and b } }
158
+ else
159
+ false
160
+ end
161
+ else
162
+ eq_without_multiarray other
163
+ end
164
+ else
165
+ false
166
+ end
167
+ end
168
+
169
+ alias_method_chain :==, :multiarray, :eq
170
+
171
+ def min
172
+ inject { |a,b| a.minor b }
173
+ end
174
+
175
+ def max
176
+ inject { |a,b| a.major b }
177
+ end
178
+
179
+ # Apply accumulative operation over elements diagonally
180
+ #
181
+ # This method is used internally to implement convolutions.
182
+ #
183
+ # @param [Object,Node] initial Initial value.
184
+ # @option options [Variable] :var1 First variable defining operation.
185
+ # @option options [Variable] :var2 Second variable defining operation.
186
+ # @option options [Variable] :block (yield( var1, var2 )) The operation to
187
+ # apply diagonally.
188
+ # @yield Optional operation to apply diagonally.
189
+ #
190
+ # @return [Node] Result of operation.
191
+ #
192
+ # @see #convolve
193
+ #
194
+ # @private
195
+ def diagonal( initial = nil, options = {} )
196
+ if dimension == 0
197
+ demand
198
+ else
199
+ if initial
200
+ unless initial.is_a? Node
201
+ initial = Node.match( initial ).new initial
202
+ end
203
+ initial_typecode = initial.typecode
204
+ else
205
+ initial_typecode = typecode
206
+ end
207
+ index0 = Variable.new Hornetseye::INDEX( nil )
208
+ index1 = Variable.new Hornetseye::INDEX( nil )
209
+ index2 = Variable.new Hornetseye::INDEX( nil )
210
+ var1 = options[ :var1 ] || Variable.new( initial_typecode )
211
+ var2 = options[ :var2 ] || Variable.new( typecode )
212
+ block = options[ :block ] || yield( var1, var2 )
213
+ value = element( index1 ).element( index2 ).
214
+ diagonal initial, :block => block, :var1 => var1, :var2 => var2
215
+ term = Diagonal.new( value, index0, index1, index2, initial,
216
+ block, var1, var2 )
217
+ index0.size[] ||= index1.size[]
218
+ Lambda.new( index0, term ).force
219
+ end
220
+ end
221
+
222
+ # Compute product table from two arrays
223
+ #
224
+ # Used internally to implement convolutions.
225
+ #
226
+ # @param [Node] filter Filter to form product table with.
227
+ #
228
+ # @return [Node] Result of operation.
229
+ #
230
+ # @see #convolve
231
+ #
232
+ # @private
233
+ def product( filter )
234
+ if dimension == 0
235
+ self * filter
236
+ else
237
+ Hornetseye::lazy { |i,j| self[j].product filter[i] }
238
+ end
239
+ end
240
+
241
+ # Convolution with other array of same dimension
242
+ #
243
+ # @param [Node] filter Filter to convolve with.
244
+ #
245
+ # @return [Node] Result of convolution.
246
+ def convolve( filter )
247
+ product( filter ).diagonal { |s,x| s + x }
248
+ end
69
249
 
70
250
  end
71
251