multiarray 0.5.0 → 0.5.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.
@@ -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