multiarray 0.5.1 → 0.5.2

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.
@@ -19,6 +19,14 @@ module Hornetseye
19
19
 
20
20
  class Inject < Node
21
21
 
22
+ class << self
23
+
24
+ def finalised?
25
+ false
26
+ end
27
+
28
+ end
29
+
22
30
  def initialize( value, index, initial, block, var1, var2 )
23
31
  @value, @index, @initial, @block, @var1, @var2 =
24
32
  value, index, initial, block, var1, var2
@@ -41,7 +49,7 @@ module Hornetseye
41
49
  end
42
50
 
43
51
  def array_type
44
- @value.array_type
52
+ @value.to_type( @block.typecode ).array_type
45
53
  end
46
54
 
47
55
  # Reevaluate computation
@@ -53,7 +61,7 @@ module Hornetseye
53
61
  # @private
54
62
  def demand
55
63
  if @initial
56
- retval = @initial.simplify # !!!
64
+ retval = @initial.to_type( typecode ).simplify # !!!
57
65
  offset = INT.new 0
58
66
  else
59
67
  retval = @value.subst( @index => INT.new( 0 ) ).simplify # !!!
@@ -56,8 +56,7 @@ module Hornetseye
56
56
  def strip
57
57
  meta_vars, meta_values, var = @index.strip
58
58
  vars, values, term = @term.subst( @index => var ).strip
59
- return vars + meta_vars, values + meta_values,
60
- Lambda.new( var, term )
59
+ return vars + meta_vars, values + meta_values, Lambda.new( var, term )
61
60
  end
62
61
 
63
62
  def subst( hash )
@@ -100,12 +99,23 @@ module Hornetseye
100
99
  #
101
100
  # @return [Node,Object] Result of inserting +i+ for lambda argument.
102
101
  def element( i )
103
- i = Node.match( i ).new i unless i.is_a? Node
102
+ unless i.is_a? Node
103
+ unless ( 0 ... shape.last ).member? i
104
+ raise "Index must be in 0 ... #{shape.last} (was #{i})"
105
+ end
106
+ i = Node.match( i ).new i
107
+ end
104
108
  i.size.store @index.size if @index.size.get and i.is_a? Variable
105
109
  @term.subst @index => i
106
110
  end
107
111
 
108
112
  def slice( start, length )
113
+ unless start.is_a?( Node ) or length.is_a?( Node )
114
+ if start < 0 or start + length > shape.last
115
+ raise "Range must be in 0 ... #{shape.last} " +
116
+ "(was #{start} ... #{start + length})"
117
+ end
118
+ end
109
119
  start = Node.match( start ).new start unless start.is_a? Node
110
120
  length = Node.match( length ).new length unless length.is_a? Node
111
121
  index = Variable.new Hornetseye::INDEX( length )
@@ -113,10 +123,18 @@ module Hornetseye
113
123
  skip( index, start ) ).unroll
114
124
  end
115
125
 
126
+ def decompose
127
+ Lambda.new @index, @term.decompose
128
+ end
129
+
116
130
  def compilable?
117
131
  @term.compilable?
118
132
  end
119
133
 
134
+ def finalised?
135
+ @term.finalised?
136
+ end
137
+
120
138
  end
121
139
 
122
140
  end
@@ -93,6 +93,14 @@ module Hornetseye
93
93
  Lookup.new @p.slice( start, length ), @index, @stride
94
94
  end
95
95
 
96
+ def decompose
97
+ if typecode < Composite
98
+ Lookup.new @p.decompose, @index, @stride * typecode.num_elements
99
+ else
100
+ Lookup.new @p.decompose, @index, @stride
101
+ end
102
+ end
103
+
96
104
  end
97
105
 
98
106
  end
@@ -44,7 +44,10 @@ module Hornetseye
44
44
  target = a.typecode.send conversion
45
45
  target.new mod.send( op, a.simplify.get )
46
46
  else
47
- Hornetseye::UnaryMethod( mod, op, conversion ).new( a ).force
47
+ Hornetseye::ElementWise( lambda { |x| mod.send op, x },
48
+ "#{mod}.#{op}",
49
+ lambda { |x| x.send conversion } ).
50
+ new( a ).force
48
51
  end
49
52
  else
50
53
  send "#{op}_without_hornetseye", a
@@ -62,14 +65,17 @@ module Hornetseye
62
65
  mod.module_eval do
63
66
  define_method( "#{op}_with_hornetseye" ) do |a,b|
64
67
  if a.is_a? Node or b.is_a? Node
65
- a = Node.match( a, b.typecode ).new a unless a.is_a? Node
66
- b = Node.match( b, a.typecode ).new b unless b.is_a? Node
68
+ a = Node.match( a, b ).new a unless a.is_a? Node
69
+ b = Node.match( b, a ).new b unless b.is_a? Node
67
70
  if a.dimension == 0 and a.variables.empty? and
68
71
  b.dimension == 0 and b.variables.empty?
69
72
  target = a.typecode.send coercion, b.typecode
70
73
  target.new mod.send( op, a.simplify.get, b.simplify.get )
71
74
  else
72
- Hornetseye::BinaryMethod( mod, op, coercion ).new( a, b ).force
75
+ Hornetseye::ElementWise( lambda { |x,y| mod.send op, x, y },
76
+ "#{mod}.#{op}",
77
+ lambda { |t,u| t.send coercion, u } ).
78
+ new( a, b ).force
73
79
  end
74
80
  else
75
81
  send "#{op}_without_hornetseye", a, b
@@ -135,10 +135,18 @@ module Hornetseye
135
135
  BOOL
136
136
  end
137
137
 
138
+ def scalar
139
+ self
140
+ end
141
+
142
+ def float_scalar
143
+ float.scalar
144
+ end
145
+
138
146
  # Get boolean-based datatype for binary operation
139
147
  #
140
148
  # @return [Class] Returns +BOOL+.
141
- def bool_binary( other )
149
+ def coercion_bool( other )
142
150
  other.coercion( self ).bool
143
151
  end
144
152
 
@@ -146,7 +154,7 @@ module Hornetseye
146
154
  self
147
155
  end
148
156
 
149
- def largeint( other )
157
+ def coercion_maxint( other )
150
158
  coercion( other ).maxint
151
159
  end
152
160
 
@@ -161,6 +169,23 @@ module Hornetseye
161
169
  other.coercion( self ).float
162
170
  end
163
171
 
172
+ def byte
173
+ BYTE
174
+ end
175
+
176
+ def coercion_byte( other )
177
+ coercion( other ).byte
178
+ end
179
+
180
+ def cond( a, b )
181
+ t = a.coercion b
182
+ Hornetseye::MultiArray( t.typecode, *shape ).coercion t
183
+ end
184
+
185
+ def to_type( dest )
186
+ dest
187
+ end
188
+
164
189
  # Get variables contained in this datatype
165
190
  #
166
191
  # @return [Set] Returns +Set[]+.
@@ -212,6 +237,10 @@ module Hornetseye
212
237
  true
213
238
  end
214
239
 
240
+ def finalised?
241
+ true
242
+ end
243
+
215
244
  end
216
245
 
217
246
  # Array type of this term
@@ -235,6 +264,10 @@ module Hornetseye
235
264
  array_type.typecode
236
265
  end
237
266
 
267
+ def basetype
268
+ array_type.basetype
269
+ end
270
+
238
271
  # Get shape of this term
239
272
  #
240
273
  # @return [Array<Integer>] Returns +array_type.shape+.
@@ -367,6 +400,12 @@ module Hornetseye
367
400
  'Node()'
368
401
  end
369
402
 
403
+ def dup
404
+ retval = array_type.new
405
+ retval[] = self
406
+ retval
407
+ end
408
+
370
409
  # Substitute variables
371
410
  #
372
411
  # Substitute the variables with the values given in the hash.
@@ -389,6 +428,10 @@ module Hornetseye
389
428
  typecode.compilable?
390
429
  end
391
430
 
431
+ def finalised?
432
+ self.class.finalised?
433
+ end
434
+
392
435
  # Retrieve value of array element(s)
393
436
  #
394
437
  # @param [Array<Integer>] *indices Index/indices to select element.
@@ -407,6 +450,21 @@ module Hornetseye
407
450
  end
408
451
  end
409
452
 
453
+ def check_shape( *args )
454
+ args.each do |arg|
455
+ if dimension < arg.dimension
456
+ raise "#{arg.array_type.inspect} has #{arg.dimension} dimension(s) " +
457
+ "but should not have more than #{dimension}"
458
+ end
459
+ if ( shape + arg.shape ).all? { |s| s.is_a? Integer }
460
+ if shape.last( arg.dimension ) != arg.shape
461
+ raise "#{arg.array_type.inspect} has shape #{arg.shape.inspect} " +
462
+ "(does not match last value(s) of #{shape.inspect})"
463
+ end
464
+ end
465
+ end
466
+ end
467
+
410
468
  # Assign value to array element(s)
411
469
  #
412
470
  # @overload []=( *indices, value )
@@ -419,7 +477,12 @@ module Hornetseye
419
477
  value = indices.pop
420
478
  value = Node.match( value ).new value unless value.is_a? Node
421
479
  if indices.empty?
422
- store value
480
+ check_shape value
481
+ unless compilable? and value.compilable? and dimension > 0
482
+ store value
483
+ else
484
+ GCCFunction.run self, value
485
+ end
423
486
  else
424
487
  if indices.last.is_a? Range
425
488
  view = slice indices.last.min, indices.last.size
@@ -463,15 +526,6 @@ module Hornetseye
463
526
  self
464
527
  end
465
528
 
466
- # Check whether mode of computation is lazy
467
- #
468
- # @see #lazy
469
- #
470
- # @private
471
- def lazy?
472
- ( dimension > 0 and Thread.current[ :lazy ] ) or not variables.empty?
473
- end
474
-
475
529
  # Force delayed computation unless in lazy mode
476
530
  #
477
531
  # @return [Node,Object] Result of computation
@@ -480,8 +534,10 @@ module Hornetseye
480
534
  #
481
535
  # @private
482
536
  def force
483
- if lazy?
537
+ if ( dimension > 0 and Thread.current[ :lazy ] ) or not variables.empty?
484
538
  self
539
+ elsif finalised?
540
+ get
485
541
  else
486
542
  unless compilable?
487
543
  Hornetseye::lazy do
@@ -490,7 +546,7 @@ module Hornetseye
490
546
  retval.get
491
547
  end
492
548
  else
493
- GCCFunction.run( self ).get
549
+ GCCFunction.run( pointer_type.new, self ).get
494
550
  end
495
551
  end
496
552
  end
@@ -503,6 +559,11 @@ module Hornetseye
503
559
  #
504
560
  # @private
505
561
  def simplify
562
+ #if Thread.current[ :function ] and dimension == 0
563
+ # demand.dup
564
+ #else
565
+ # demand
566
+ #end
506
567
  dimension == 0 ? demand.dup : demand
507
568
  end
508
569
 
@@ -517,10 +578,14 @@ module Hornetseye
517
578
  if other.is_a? Node
518
579
  return other, self
519
580
  else
520
- return Node.match( other, typecode ).new( other ), self
581
+ return Node.match( other, self ).new( other ), self
521
582
  end
522
583
  end
523
584
 
585
+ def decompose
586
+ self
587
+ end
588
+
524
589
  end
525
590
 
526
591
  end
@@ -25,7 +25,9 @@ module Hornetseye
25
25
  target = typecode.send conversion
26
26
  target.new simplify.get.send( op )
27
27
  else
28
- Hornetseye::UnaryOp( op, conversion ).new( self ).force
28
+ Hornetseye::ElementWise( lambda { |x| x.send op }, op,
29
+ lambda { |t| t.send conversion } ).
30
+ new( self ).force
29
31
  end
30
32
  end
31
33
  end
@@ -42,7 +44,9 @@ module Hornetseye
42
44
  target = array_type.send coercion, other.array_type
43
45
  target.new simplify.get.send( op, other.simplify.get )
44
46
  else
45
- Hornetseye::BinaryOp( op, coercion ).new( self, other ).force
47
+ Hornetseye::ElementWise( lambda { |x,y| x.send op, y }, op,
48
+ lambda { |t,u| t.send coercion, u } ).
49
+ new( self, other ).force
46
50
  end
47
51
  end
48
52
  end
@@ -54,29 +58,37 @@ module Hornetseye
54
58
  define_unary_op :not, :bool
55
59
  define_unary_op :~
56
60
  define_unary_op :-@
61
+ define_unary_op :conj
62
+ define_unary_op :abs, :scalar
63
+ define_unary_op :arg, :float_scalar
57
64
  define_unary_op :floor
58
65
  define_unary_op :ceil
59
66
  define_unary_op :round
67
+ define_unary_op :r, :scalar
68
+ define_unary_op :g, :scalar
69
+ define_unary_op :b, :scalar
70
+ define_unary_op :real, :scalar
71
+ define_unary_op :imag, :scalar
60
72
  define_binary_op :+
61
73
  define_binary_op :-
62
74
  define_binary_op :*
63
- define_binary_op :**, :largeint
75
+ define_binary_op :**, :coercion_maxint
64
76
  define_binary_op :/
65
77
  define_binary_op :%
66
- define_binary_op :and, :bool_binary
67
- define_binary_op :or, :bool_binary
78
+ define_binary_op :and, :coercion_bool
79
+ define_binary_op :or, :coercion_bool
68
80
  define_binary_op :&
69
81
  define_binary_op :|
70
82
  define_binary_op :^
71
83
  define_binary_op :<<
72
84
  define_binary_op :>>
73
- define_binary_op :eq, :bool_binary
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
85
+ define_binary_op :eq, :coercion_bool
86
+ define_binary_op :ne, :coercion_bool
87
+ define_binary_op :<=, :coercion_bool
88
+ define_binary_op :<, :coercion_bool
89
+ define_binary_op :>=, :coercion_bool
90
+ define_binary_op :>, :coercion_bool
91
+ define_binary_op :<=>, :coercion_byte
80
92
  define_binary_op :minor
81
93
  define_binary_op :major
82
94
 
@@ -84,6 +96,42 @@ module Hornetseye
84
96
  self
85
97
  end
86
98
 
99
+ def to_type( dest )
100
+ if dimension == 0 and variables.empty?
101
+ target = typecode.to_type dest
102
+ target.new simplify.get
103
+ else
104
+ key = "to_#{dest.to_s.downcase}"
105
+ Hornetseye::ElementWise( lambda { |x| x.to_type dest }, key,
106
+ lambda { |t| t.to_type dest } ).new( self ).force
107
+ end
108
+ end
109
+
110
+ def conditional( a, b )
111
+ unless a.is_a? Node
112
+ a = Node.match( a, b.is_a?( Node ) ? b : nil ).new a
113
+ end
114
+ unless b.is_a? Node
115
+ b = Node.match( b, a.is_a?( Node ) ? a : nil ).new b
116
+ end
117
+ if dimension == 0 and variables.empty? and
118
+ a.dimension == 0 and a.variables.empty? and
119
+ b.dimension == 0 and b.variables.empty?
120
+ target = array_type.cond a.array_type, b.array_type
121
+ target.new simplify.get.conditional( a.simplify.get, b.simplify.get )
122
+ else
123
+ Hornetseye::ElementWise( lambda { |x,y,z| x.conditional y, z }, :conditional,
124
+ lambda { |t,u,v| t.cond u, v } ).
125
+ new( self, a, b ).force
126
+ end
127
+ end
128
+
129
+ def <=>( other )
130
+ Hornetseye::lazy do
131
+ ( self < other ).conditional -1, ( self > other ).conditional( 1, 0 )
132
+ end
133
+ end
134
+
87
135
  # Lazy transpose of array
88
136
  #
89
137
  # Lazily compute transpose by swapping indices according to the specified
@@ -123,6 +171,13 @@ module Hornetseye
123
171
  end
124
172
  end
125
173
 
174
+ def collect( &action )
175
+ var = Variable.new typecode
176
+ block = action.call var
177
+ conversion = lambda { |t| t.to_type action.call( Variable.new( t.typecode ) ) }
178
+ Hornetseye::ElementWise( action, block.to_s, conversion ).new( self ).force
179
+ end
180
+
126
181
  def inject( initial = nil, options = {} )
127
182
  unless initial.nil?
128
183
  initial = Node.match( initial ).new initial unless initial.is_a? Node
@@ -231,6 +286,11 @@ module Hornetseye
231
286
  #
232
287
  # @private
233
288
  def product( filter )
289
+ filter = Node.match( filter, typecode ).new filter unless filter.is_a? Node
290
+ if dimension != filter.dimension
291
+ raise "Filter has #{filter.dimension} dimension(s) but should " +
292
+ "have #{dimension}"
293
+ end
234
294
  if dimension == 0
235
295
  self * filter
236
296
  else
@@ -244,9 +304,130 @@ module Hornetseye
244
304
  #
245
305
  # @return [Node] Result of convolution.
246
306
  def convolve( filter )
307
+ filter = Node.match( filter, typecode ).new filter unless filter.is_a? Node
247
308
  product( filter ).diagonal { |s,x| s + x }
248
309
  end
249
310
 
311
+ def r_with_decompose
312
+ if typecode < RGB_
313
+ decompose.roll.element 0
314
+ elsif typecode == OBJECT
315
+ r_without_decompose
316
+ else
317
+ self
318
+ end
319
+ end
320
+
321
+ alias_method_chain :r, :decompose
322
+
323
+ def r=( value )
324
+ if typecode < RGB_
325
+ decompose.roll[ 0 ] = value
326
+ elsif typecode == OBJECT
327
+ self[] = Hornetseye::lazy do
328
+ value * RGB.new( 1, 0, 0 ) + g * RGB.new( 0, 1, 0 ) + b * RGB.new( 0, 0, 1 )
329
+ end
330
+ else
331
+ raise "Cannot assign red channel to object of type #{array_type.inspect}"
332
+ end
333
+ end
334
+
335
+ def g_with_decompose
336
+ if typecode < RGB_
337
+ decompose.roll.element 1
338
+ elsif typecode == OBJECT
339
+ g_without_decompose
340
+ else
341
+ self
342
+ end
343
+ end
344
+
345
+ alias_method_chain :g, :decompose
346
+
347
+ def g=( value )
348
+ if typecode < RGB_
349
+ decompose.roll[ 1 ] = value
350
+ elsif typecode == OBJECT
351
+ self[] = Hornetseye::lazy do
352
+ r * RGB.new( 1, 0, 0 ) + value * RGB.new( 0, 1, 0 ) + b * RGB.new( 0, 0, 1 )
353
+ end
354
+ else
355
+ raise "Cannot assign green channel to object of type #{array_type.inspect}"
356
+ end
357
+ end
358
+
359
+ def b_with_decompose
360
+ if typecode < RGB_
361
+ decompose.roll.element 2
362
+ elsif typecode == OBJECT
363
+ b_without_decompose
364
+ else
365
+ self
366
+ end
367
+ end
368
+
369
+ alias_method_chain :b, :decompose
370
+
371
+ def b=( value )
372
+ if typecode < RGB_
373
+ decompose.roll[ 2 ] = value
374
+ elsif typecode == OBJECT
375
+ self[] = Hornetseye::lazy do
376
+ r * RGB.new( 1, 0, 0 ) + g * RGB.new( 0, 1, 0 ) + value * RGB.new( 0, 0, 1 )
377
+ end
378
+ else
379
+ raise "Cannot assign blue channel to object of type #{array_type.inspect}"
380
+ end
381
+ end
382
+
383
+ def real_with_decompose
384
+ if typecode < COMPLEX_
385
+ decompose.roll.element 0
386
+ elsif typecode == OBJECT
387
+ real_without_decompose
388
+ else
389
+ self
390
+ end
391
+ end
392
+
393
+ alias_method_chain :real, :decompose
394
+
395
+ def real=( value )
396
+ if typecode < COMPLEX_
397
+ decompose.roll[ 0 ] = value
398
+ elsif typecode == OBJECT
399
+ self[] = Hornetseye::lazy do
400
+ value + imag * ::Complex::I
401
+ end
402
+ else
403
+ self[] = value
404
+ end
405
+ end
406
+
407
+ def imag_with_decompose
408
+ if typecode < COMPLEX_
409
+ decompose.roll.element 1
410
+ elsif typecode == OBJECT
411
+ imag_without_decompose
412
+ else
413
+ Hornetseye::lazy( *shape ) { typecode.new( 0 ) }
414
+ end
415
+ end
416
+
417
+ alias_method_chain :imag, :decompose
418
+
419
+ def imag=( value )
420
+ if typecode < COMPLEX_
421
+ decompose.roll[ 1 ] = value
422
+ elsif typecode == OBJECT
423
+ self[] = Hornetseye::lazy do
424
+ real + value * ::Complex::I
425
+ end
426
+ else
427
+ raise "Cannot assign imaginary values to object of type #{array_type.inspect}"
428
+ end
429
+ end
430
+
250
431
  end
251
432
 
252
433
  class Node