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
221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/carray/block_iterator.rb', line 221 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
247 248 249 |
# File 'lib/carray/block_iterator.rb', line 247 def count_masked elements - count_not_masked end |
#count_not_masked ⇒ CArray
239 240 241 |
# File 'lib/carray/block_iterator.rb', line 239 def count_not_masked fold(:count_not_masked) end |
#each({ |tile| ... }) {|tile| ... } ⇒ Enumerator, self
454 455 456 457 458 459 460 |
# File 'lib/carray/block_iterator.rb', line 454 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
257 258 259 260 261 262 |
# File 'lib/carray/block_iterator.rb', line 257 def elements sz = @sizes.inject(1) { |p, b| p * b } out = CArray.int64(*@shape) out[] = sz out end |
#map({ |tile| ... }) {|tile| ... } ⇒ CArray, Enumerator
500 501 502 503 504 505 506 507 508 |
# File 'lib/carray/block_iterator.rb', line 500 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
298 |
# File 'lib/carray/block_iterator.rb', line 298 def max_addr; winner_addr(:max_index); end |
#median ⇒ Object
401 402 403 |
# File 'lib/carray/block_iterator.rb', line 401 def median order_stat { |v, axis| v.median(axis: axis) } end |
#min_addr ⇒ CArray
293 |
# File 'lib/carray/block_iterator.rb', line 293 def min_addr; winner_addr(:min_index); end |
#minmax(min_count: nil, fill_value: nil) ⇒ Array<CArray>
269 270 271 272 273 274 |
# File 'lib/carray/block_iterator.rb', line 269 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
204 205 206 207 208 209 210 211 212 |
# File 'lib/carray/block_iterator.rb', line 204 [:sum, :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+
409 410 411 |
# File 'lib/carray/block_iterator.rb', line 409 def percentile (*pers) order_stat { |v, axis| v.percentile(*pers, axis: axis) } end |
#quantile ⇒ Array<CArray>
416 417 418 |
# File 'lib/carray/block_iterator.rb', line 416 def quantile order_stat { |v, axis| v.quantile(axis: axis) } end |
#reduce({ |tile| ... }) {|tile| ... } ⇒ CArray #reduce(init) ⇒ CArray
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
# File 'lib/carray/block_iterator.rb', line 473 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
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/carray/block_iterator.rb', line 338 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
365 366 367 |
# File 'lib/carray/block_iterator.rb', line 365 def wmean (weights) weighted(weights) { |view, w| view.wmean(w, axis: @tile_axes) } end |
#wsum(weights) ⇒ CArray
358 359 360 |
# File 'lib/carray/block_iterator.rb', line 358 def wsum (weights) weighted(weights) { |view, w| view.wsum(w, axis: @tile_axes) } end |