multiarray 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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