Class: CABlockIterator
- Inherits:
-
CAIterator
- Object
- CAIterator
- CABlockIterator
- Defined in:
- lib/carray/block_iterator.rb
Overview
Non-overlapping tile reduction dispatcher — the Block member of the
iterator family (sibling of CASlabIterator / CAWindowIterator /
CACategoricalIterator). Where a window iterator folds an overlapping
window per anchor, a block iterator folds each non-overlapping tile of a
fixed per-axis size, so the result is a tile grid: pooling, downsampling,
block statistics.
Obtained from CArray#blocks, not constructed directly.
Instance Attribute Summary collapse
-
#source ⇒ CArray
readonly
Returns the array being tiled (with any leading offset already applied).
Attributes inherited from CAIterator
Instance Method Summary collapse
-
#count(v = <none>) ⇒ CArray
Per-tile count.
-
#count_masked ⇒ CArray
Per-tile count of masked cells.
-
#count_not_masked ⇒ CArray
Per-tile count of present (non-masked) cells.
-
#each({ |tile| ... }) {|tile| ... } ⇒ Enumerator, self
Yields each tile as a uniform
Π b_i-shaped CArray (partial edge tiles have their out-of-bounds cells masked). -
#elements ⇒ CArray
Tile cell count (structural, mask-independent): the constant tile size
Π b_i, shaped like the tile grid. -
#initialize(source, *blocks) ⇒ CABlockIterator
constructor
Builds a block iterator tiling
sourcewith a per-axis tile size. -
#map({ |tile| ... }) {|tile| ... } ⇒ CArray, Enumerator
Per-tile element-wise transform: the block receives each tile (a uniform
Π b_i-shaped CArray, masked at partial edges) and returns a same-shaped tile (or a scalar to broadcast). -
#max_addr ⇒ CArray
Per-tile flat source address of the maximum.
-
#median ⇒ Object
Per-tile median.
-
#min_addr ⇒ CArray
Per-tile flat SOURCE address of the minimum — which cell of the source holds it, so
source.reshape(source.elements)[bi.min_addr]are the tile minima. -
#minmax(min_count: nil, fill_value: nil) ⇒ Array<CArray>
Per-tile
[min, max](two tile-grid-shaped CArrays, a single fused pass). - #op ⇒ Object
-
#percentile(*pers) ⇒ CArray+
Per-tile percentile(s).
-
#quantile ⇒ Array<CArray>
Per-tile five-number summary
[min, Q1, median, Q3, max](five CArrays). - #reduce(*args, data_type: nil, &blk) ⇒ Object
-
#sort_addr ⇒ CArray
Per-tile sort by flat SOURCE address, source-shaped: each tile's cells hold that tile's source addresses in ascending-value order (reading the tile row-major gives the sorted addresses), so
source.reshape(source.elements)[bi.sort_addr]is the source sorted within each tile. -
#wmean(weights) ⇒ CArray
Per-tile weighted mean;
weightsshaped like one full tile. -
#wsum(weights) ⇒ CArray
Per-tile weighted sum;
weightsis shaped like one full tile (Π b_i).
Constructor Details
#initialize(source, *blocks) ⇒ CABlockIterator
Returns a new instance of CABlockIterator.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/carray/block_iterator.rb', line 69 def initialize (source, *blocks) blocks = blocks[0] if blocks.size == 1 && blocks[0].is_a?(Array) unless blocks.size == source.ndim raise ArgumentError, "blocks: expected #{source.ndim} tile sizes (one per axis), " \ "got #{blocks.size}" end @sndim = source.ndim offsets = Array.new(@sndim, 0) @sizes = Array.new(@sndim) blocks.each_with_index do |b, i| if b.is_a?(Range) offsets[i] = b.begin @sizes[i] = b.end - b.begin + (b.exclude_end? ? 0 : 1) else @sizes[i] = Integer(b) end if @sizes[i] < 1 raise ArgumentError, "blocks: tile size on axis #{i} must be >= 1" end end # Absorb a leading offset with a zero-copy pre-slice, so the tile geometry # below always starts at index 0. @source = if offsets.all?(&:zero?) source else source[*offsets.map { |o| o..-1 }] end n = @source.shape @q = @sndim.times.map { |i| n[i] / @sizes[i] } # full tiles per axis @r = @sndim.times.map { |i| n[i] % @sizes[i] } # remainder per axis # Ceil tile grid: a partial edge tile adds one grid cell on that axis. @shape = @sndim.times.map { |i| @q[i] + (@r[i] > 0 ? 1 : 0) } @ndim = @shape.size # Trailing tile axes of a block_view: [ndim .. 2*ndim-1]. @tile_axes = (@sndim...(2 * @sndim)).to_a self end |
Instance Attribute Details
#source ⇒ CArray (readonly)
118 119 120 |
# File 'lib/carray/block_iterator.rb', line 118 def source @source end |
Instance Method Details
#count(v = <none>) ⇒ CArray
227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/carray/block_iterator.rb', line 227 def count (*args) return count_not_masked if args.empty? out = nil each_region do |strip_ranges, tiles, out_ranges| view = @source[*strip_ranges].block_view(*tiles) # block_view is a CAStride, so #count is not shadowed; dispatch # CArray#count explicitly anyway, matching the family regularity. red = CArray.instance_method(:count).bind_call(view, *args, axis: @tile_axes) out ||= CArray.new(red.data_type, @shape) out[*out_ranges] = red end out end |
#count_masked ⇒ CArray
253 254 255 |
# File 'lib/carray/block_iterator.rb', line 253 def count_masked elements - count_not_masked end |
#count_not_masked ⇒ CArray
245 246 247 |
# File 'lib/carray/block_iterator.rb', line 245 def count_not_masked fold(:count_not_masked) end |
#each({ |tile| ... }) {|tile| ... } ⇒ Enumerator, self
464 465 466 467 468 469 470 |
# File 'lib/carray/block_iterator.rb', line 464 def each return to_enum(:each) unless block_given? tgv = tile_grid_view nils = Array.new(@sndim, nil) CArray.each_index(*@shape) { |*g| yield tgv[*g, *nils] } self end |
#elements ⇒ CArray
263 264 265 266 267 268 |
# File 'lib/carray/block_iterator.rb', line 263 def elements sz = @sizes.inject(1) { |p, b| p * b } out = CArray.int64(*@shape) out[] = sz out end |
#map({ |tile| ... }) {|tile| ... } ⇒ CArray, Enumerator
510 511 512 513 514 515 516 517 518 |
# File 'lib/carray/block_iterator.rb', line 510 def map return to_enum(:map) unless block_given? pout = CArray.new(padded_source.data_type, padded_source.shape) pgv = pout.block_view(*@sizes) tgv = tile_grid_view nils = Array.new(@sndim, nil) CArray.each_index(*@shape) { |*g| pgv[*g, *nils] = yield(tgv[*g, *nils]) } pout[*@sndim.times.map { |i| 0...@source.shape[i] }].copy end |
#max_addr ⇒ CArray
308 |
# File 'lib/carray/block_iterator.rb', line 308 def max_addr; winner_addr(:max_index); end |
#median ⇒ Object
411 412 413 |
# File 'lib/carray/block_iterator.rb', line 411 def median order_stat { |v, axis| v.median(axis: axis) } end |
#min_addr ⇒ CArray
303 |
# File 'lib/carray/block_iterator.rb', line 303 def min_addr; winner_addr(:min_index); end |
#minmax(min_count: nil, fill_value: nil) ⇒ Array<CArray>
275 276 277 278 279 280 |
# File 'lib/carray/block_iterator.rb', line 275 def minmax (min_count: nil, fill_value: nil) kw = {} kw[:min_count] = min_count unless min_count.nil? kw[:fill_value] = fill_value unless fill_value.nil? assemble { |view, _| view.minmax(axis: @tile_axes, **kw) } end |
#cumsum ⇒ CArray #cumprod ⇒ CArray #cummax ⇒ CArray #cummin ⇒ CArray #cumcount ⇒ CArray
210 211 212 213 214 215 216 217 218 |
# File 'lib/carray/block_iterator.rb', line 210 [:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any, :variancep, :stddevp].each do |op| define_method(op) do |min_count: nil, fill_value: nil| kw = {} kw[:min_count] = min_count unless min_count.nil? kw[:fill_value] = fill_value unless fill_value.nil? fold(op, **kw) end end |
#percentile(*pers) ⇒ CArray+
419 420 421 |
# File 'lib/carray/block_iterator.rb', line 419 def percentile (*pers) order_stat { |v, axis| v.percentile(*pers, axis: axis) } end |
#quantile ⇒ Array<CArray>
426 427 428 |
# File 'lib/carray/block_iterator.rb', line 426 def quantile order_stat { |v, axis| v.quantile(axis: axis) } end |
#reduce({ |tile| ... }) {|tile| ... } ⇒ CArray #reduce(init) ⇒ CArray
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 |
# File 'lib/carray/block_iterator.rb', line 483 def reduce (*args, data_type: nil, &blk) raise LocalJumpError, "no block given (yield)" unless blk out = CArray.new(data_type || CA_OBJECT, @shape) tgv = tile_grid_view nils = Array.new(@sndim, nil) if args.empty? CArray.each_index(*@shape) { |*g| out[*g] = blk.call(tgv[*g, *nils]) } else init = args[0] CArray.each_index(*@shape) do |*g| acc = init tgv[*g, *nils].each { |e| acc = blk.call(acc, e) } out[*g] = acc end end out end |
#sort_addr ⇒ CArray
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 |
# File 'lib/carray/block_iterator.rb', line 348 def sort_addr saddr = CArray.int64(*@source.shape).seq! out = CArray.int64(*@source.shape) out[] = UNDEF each_region do |strip_ranges, tiles, _out_ranges| vview = @source[*strip_ranges].block_view(*tiles) sview = saddr[*strip_ranges].block_view(*tiles) grid = (0...@sndim).map { |i| vview.shape[i] } cells = tiles.inject(1) { |p, t| p * t } order = vview.copy.reshape(*(grid + [cells])).sort_addr(axis: @sndim) src_sorted = sview.reshape(*(grid + [cells])).take_along_axis(order, axis: @sndim) out[*strip_ranges].block_view(*tiles)[] = src_sorted.reshape(*(grid + tiles)) end out end |
#wmean(weights) ⇒ CArray
375 376 377 |
# File 'lib/carray/block_iterator.rb', line 375 def wmean (weights) weighted(weights) { |view, w| view.wmean(w, axis: @tile_axes) } end |
#wsum(weights) ⇒ CArray
368 369 370 |
# File 'lib/carray/block_iterator.rb', line 368 def wsum (weights) weighted(weights) { |view, w| view.wsum(w, axis: @tile_axes) } end |