carray 2.0.0 → 3.0.0
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.
- checksums.yaml +4 -4
- data/.yardopts +5 -25
- data/CHANGELOG.md +16 -0
- data/LICENSE +1 -1
- data/NEWS.md +3 -0
- data/README.md +128 -44
- data/carray.gemspec +22 -24
- data/ext/ca_array_pool.c +91 -0
- data/ext/ca_axis_descriptor.h +186 -0
- data/ext/ca_axis_dispatch.c +924 -0
- data/ext/ca_axis_group.c +1208 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +125 -0
- data/ext/ca_binop_dispatch.h +159 -0
- data/ext/ca_categorical_iterator.c +1375 -0
- data/ext/ca_compare.c +94 -0
- data/ext/ca_compare.h +26 -0
- data/ext/ca_composite_dispatch.c +414 -0
- data/ext/ca_composite_dispatch.h +116 -0
- data/ext/ca_for_buffer.h +96 -0
- data/ext/ca_for_each_element.h +241 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4321 -0
- data/ext/ca_kernel_iterator.h +2603 -0
- data/ext/ca_moncmp_dispatch.c +37 -0
- data/ext/ca_moncmp_dispatch.h +62 -0
- data/ext/ca_monop_dispatch.c +200 -0
- data/ext/ca_monop_dispatch.h +235 -0
- data/ext/ca_obj_array.c +355 -359
- data/ext/ca_obj_bincmp.c +809 -0
- data/ext/ca_obj_binop.c +892 -0
- data/ext/ca_obj_bitarray.c +369 -164
- data/ext/ca_obj_bitfield.c +294 -234
- data/ext/ca_obj_block.c +189 -711
- data/ext/ca_obj_byte_swap.c +766 -0
- data/ext/ca_obj_const_string.c +965 -0
- data/ext/ca_obj_face.c +670 -0
- data/ext/ca_obj_face.h +247 -0
- data/ext/ca_obj_fake.c +228 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +306 -0
- data/ext/ca_obj_grid.c +858 -440
- data/ext/ca_obj_meld.c +1034 -0
- data/ext/ca_obj_moncmp.c +569 -0
- data/ext/ca_obj_monop.c +1111 -0
- data/ext/ca_obj_object.c +774 -298
- data/ext/ca_obj_record.c +468 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +569 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +616 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1296 -0
- data/ext/ca_obj_shift.c +230 -792
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2501 -0
- data/ext/ca_obj_string.c +268 -0
- data/ext/ca_obj_tile.c +614 -0
- data/ext/ca_obj_time.c +546 -0
- data/ext/ca_obj_timedelta.c +435 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +746 -0
- data/ext/ca_obj_unbound_repeat.c +208 -241
- data/ext/ca_obj_window.c +1131 -563
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_ipower.c +319 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +430 -0
- data/ext/ca_sweep_engine.h +157 -0
- data/ext/ca_transform_common.c +228 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +795 -402
- data/ext/carray_access.c +831 -711
- data/ext/carray_attribute.c +98 -330
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +283 -0
- data/ext/carray_call_cfunc.c +1360 -828
- data/ext/carray_call_cfunc.h +160 -0
- data/ext/carray_cast.c +1212 -301
- data/ext/carray_cast_func.rb +81 -40
- data/ext/carray_class.c +53 -63
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +350 -346
- data/ext/carray_copy.c +156 -268
- data/ext/carray_core.c +1342 -199
- data/ext/carray_count.c +312 -0
- data/ext/carray_data_type.c +43 -19
- data/ext/carray_element.c +585 -213
- data/ext/carray_factorize.c +2542 -0
- data/ext/carray_generate.c +230 -559
- data/ext/carray_histogram.c +490 -0
- data/ext/carray_hold.c +228 -0
- data/ext/carray_index_classifier.c +1035 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +120 -0
- data/ext/carray_kernels_bincmp.c +4445 -0
- data/ext/carray_kernels_binop.c +10979 -0
- data/ext/carray_kernels_init.c +36 -0
- data/ext/carray_kernels_map.c +3466 -0
- data/ext/carray_kernels_moncmp.c +2096 -0
- data/ext/carray_kernels_monop.c +18312 -0
- data/ext/carray_kernels_reduce_aggregate.c +25836 -0
- data/ext/carray_kernels_reduce_boolean.c +329 -0
- data/ext/carray_kernels_reduce_cumulative.c +14592 -0
- data/ext/carray_kernels_reduce_extreme.c +16947 -0
- data/ext/carray_kernels_reduce_variance.c +3909 -0
- data/ext/carray_kernels_scan.c +3692 -0
- data/ext/carray_kernels_search.c +32137 -0
- data/ext/carray_kernels_sort.c +10625 -0
- data/ext/carray_kernels_triop.c +1391 -0
- data/ext/carray_lazy.c +567 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +848 -154
- data/ext/carray_math_kernel.h +120 -0
- data/ext/carray_mathfunc.c +10 -241
- data/ext/carray_median_percentile.c +1257 -0
- data/ext/carray_memory_view.c +1625 -0
- data/ext/carray_operator.c +1526 -318
- data/ext/carray_order.c +664 -1394
- data/ext/carray_partition.c +416 -0
- data/ext/carray_random.c +518 -0
- data/ext/carray_scatter.c +357 -0
- data/ext/carray_slab.c +1219 -0
- data/ext/carray_slab.h +84 -0
- data/ext/carray_sort.c +829 -0
- data/ext/carray_sort_kernel.c +620 -0
- data/ext/carray_struct.c +695 -0
- data/ext/carray_test.c +343 -229
- data/ext/carray_undef.c +34 -17
- data/ext/carray_utils.c +175 -74
- data/ext/extconf.rb +216 -55
- data/ext/mk_call_cfunc.rb +480 -0
- data/ext/mkkernel.rb +8842 -0
- data/ext/ruby_carray.c +202 -101
- data/ext/version.h +4 -14
- data/ext/version.rb +5 -13
- data/lib/carray/arrow_tensor.rb +401 -0
- data/lib/carray/attribute.rb +166 -0
- data/lib/carray/autoload_carray.rb +220 -0
- data/lib/carray/autoload_method_extension.rb +44 -0
- data/lib/carray/axis_group.rb +711 -0
- data/lib/carray/basics.rb +481 -0
- data/lib/carray/bincount_nd.rb +358 -0
- data/lib/carray/block_iterator.rb +604 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1062 -0
- data/lib/carray/complex.rb +150 -0
- data/lib/carray/conditional.rb +216 -0
- data/lib/carray/const_string.rb +228 -0
- data/lib/carray/construct.rb +139 -328
- data/lib/carray/core_extensions.rb +240 -0
- data/lib/carray/data_type_extension.rb +233 -0
- data/lib/carray/fixlen_string.rb +95 -0
- data/lib/carray/frame/concat.rb +132 -0
- data/lib/carray/frame/convert.rb +95 -0
- data/lib/carray/frame/csv_parser.rb +211 -0
- data/lib/carray/frame/frame.rb +649 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +164 -0
- data/lib/carray/frame/join.rb +248 -0
- data/lib/carray/frame/records.rb +99 -0
- data/lib/carray/frame/sort.rb +113 -0
- data/lib/carray/frame/verbs.rb +299 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -20
- data/lib/carray/iterator.rb +57 -349
- data/lib/carray/lazy.rb +889 -0
- data/lib/carray/mask_gap_fill.rb +200 -0
- data/lib/carray/math.rb +78 -342
- data/lib/carray/meld_reduce.rb +289 -0
- data/lib/carray/methods/align_addr.rb +116 -0
- data/lib/carray/methods/bin.rb +128 -0
- data/lib/carray/methods/bincount.rb +87 -0
- data/lib/carray/methods/bit_string.rb +92 -0
- data/lib/carray/methods/broadcast.rb +63 -0
- data/lib/carray/methods/choose.rb +39 -0
- data/lib/carray/methods/composition.rb +280 -0
- data/lib/carray/methods/gather_nd.rb +206 -0
- data/lib/carray/methods/index.rb +39 -0
- data/lib/carray/methods/insert_block.rb +99 -0
- data/lib/carray/methods/is_in.rb +141 -0
- data/lib/carray/methods/join.rb +90 -0
- data/lib/carray/methods/locate_addr.rb +47 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +91 -0
- data/lib/carray/methods/mode.rb +126 -0
- data/lib/carray/methods/nunique.rb +46 -0
- data/lib/carray/methods/resize.rb +56 -0
- data/lib/carray/methods/snap.rb +156 -0
- data/lib/carray/methods/string_format.rb +57 -0
- data/lib/carray/methods/unique.rb +47 -0
- data/lib/carray/methods/value_counts.rb +71 -0
- data/lib/carray/mkmf.rb +124 -101
- data/lib/carray/runtime.rb +108 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +292 -0
- data/lib/carray/stack.rb +291 -0
- data/lib/carray/string.rb +56 -180
- data/lib/carray/string_operation_extension.rb +289 -0
- data/lib/carray/struct.rb +335 -323
- data/lib/carray/struct_builder.rb +697 -0
- data/lib/carray/table.rb +41 -2
- data/lib/carray/time.rb +2255 -38
- data/lib/carray/window_iterator.rb +655 -0
- data/lib/carray.rb +55 -57
- metadata +163 -130
- data/Rakefile +0 -51
- data/TODO.md +0 -18
- data/ext/ca_iter_block.c +0 -257
- data/ext/ca_iter_dimension.c +0 -299
- data/ext/ca_iter_window.c +0 -214
- data/ext/ca_obj_mapping.c +0 -644
- data/ext/carray_iterator.c +0 -641
- data/ext/carray_math.rb +0 -850
- data/ext/carray_numeric.c +0 -259
- data/ext/carray_sort_addr.c +0 -254
- data/ext/carray_stat.c +0 -2100
- data/ext/carray_stat_proc.rb +0 -1999
- data/ext/mkmath.rb +0 -741
- data/ext/ruby_ccomplex.c +0 -509
- data/ext/ruby_float_func.c +0 -86
- data/lib/carray/array.rb +0 -8
- data/lib/carray/autoload/autoload_base.rb +0 -19
- data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
- data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
- data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
- data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
- data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
- data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
- data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
- data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
- data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
- data/lib/carray/autoload/autoload_gem_random.rb +0 -8
- data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
- data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
- data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
- data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
- data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
- data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
- data/lib/carray/autoload/autoload_object_link.rb +0 -1
- data/lib/carray/autoload/autoload_object_pack.rb +0 -2
- data/lib/carray/autoload.rb +0 -141
- data/lib/carray/basic.rb +0 -191
- data/lib/carray/broadcast.rb +0 -101
- data/lib/carray/compose.rb +0 -315
- data/lib/carray/convert.rb +0 -115
- data/lib/carray/info.rb +0 -110
- data/lib/carray/io/imagemagick.rb +0 -235
- data/lib/carray/mask.rb +0 -102
- data/lib/carray/math/histogram.rb +0 -177
- data/lib/carray/math/recurrence.rb +0 -93
- data/lib/carray/object/ca_obj_iterator.rb +0 -50
- data/lib/carray/object/ca_obj_link.rb +0 -50
- data/lib/carray/object/ca_obj_pack.rb +0 -99
- data/lib/carray/obsolete.rb +0 -256
- data/lib/carray/ordering.rb +0 -181
- data/lib/carray/testing.rb +0 -51
- data/lib/carray/transform.rb +0 -109
- data/misc/Methods.ja.md +0 -182
- data/misc/NOTE +0 -51
- data/spec/Classes/CABitfield_spec.rb +0 -58
- data/spec/Classes/CABlockIterator_spec.rb +0 -114
- data/spec/Classes/CABlock_spec.rb +0 -205
- data/spec/Classes/CAField_spec.rb +0 -39
- data/spec/Classes/CAGrid_spec.rb +0 -75
- data/spec/Classes/CAMap_spec.rb +0 -0
- data/spec/Classes/CAMapping_spec.rb +0 -105
- data/spec/Classes/CAObject_attribute_spec.rb +0 -33
- data/spec/Classes/CAObject_spec.rb +0 -33
- data/spec/Classes/CARefer_spec.rb +0 -93
- data/spec/Classes/CARepeat_spec.rb +0 -65
- data/spec/Classes/CASelect_spec.rb +0 -22
- data/spec/Classes/CAShift_spec.rb +0 -16
- data/spec/Classes/CAStruct_spec.rb +0 -71
- data/spec/Classes/CATranspose_spec.rb +0 -60
- data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
- data/spec/Classes/CAWindow_spec.rb +0 -54
- data/spec/Classes/CAWrap_spec.rb +0 -8
- data/spec/Classes/CArray_spec.rb +0 -184
- data/spec/Classes/CScalar_spec.rb +0 -55
- data/spec/Classes/ex1.rb +0 -46
- data/spec/Features/feature_130_spec.rb +0 -19
- data/spec/Features/feature_attributes_spec.rb +0 -280
- data/spec/Features/feature_boolean_spec.rb +0 -98
- data/spec/Features/feature_broadcast.rb +0 -116
- data/spec/Features/feature_cast_function.rb +0 -19
- data/spec/Features/feature_cast_spec.rb +0 -33
- data/spec/Features/feature_class_spec.rb +0 -84
- data/spec/Features/feature_complex_spec.rb +0 -42
- data/spec/Features/feature_composite_spec.rb +0 -124
- data/spec/Features/feature_convert_spec.rb +0 -46
- data/spec/Features/feature_copy_spec.rb +0 -123
- data/spec/Features/feature_creation_spec.rb +0 -84
- data/spec/Features/feature_element_spec.rb +0 -144
- data/spec/Features/feature_extream_spec.rb +0 -54
- data/spec/Features/feature_generate_spec.rb +0 -74
- data/spec/Features/feature_index_spec.rb +0 -69
- data/spec/Features/feature_mask_spec.rb +0 -580
- data/spec/Features/feature_math_spec.rb +0 -97
- data/spec/Features/feature_order_spec.rb +0 -146
- data/spec/Features/feature_ref_store_spec.rb +0 -209
- data/spec/Features/feature_serialization_spec.rb +0 -125
- data/spec/Features/feature_stat_spec.rb +0 -397
- data/spec/Features/feature_virtual_spec.rb +0 -48
- data/spec/Features/method_eq_spec.rb +0 -81
- data/spec/Features/method_is_nan_spec.rb +0 -12
- data/spec/Features/method_map_spec.rb +0 -54
- data/spec/Features/method_max_with.rb +0 -20
- data/spec/Features/method_min_with.rb +0 -19
- data/spec/Features/method_ne_spec.rb +0 -18
- data/spec/Features/method_project_spec.rb +0 -188
- data/spec/Features/method_ref_spec.rb +0 -27
- data/spec/Features/method_round_spec.rb +0 -11
- data/spec/Features/method_s_linspace_spec.rb +0 -48
- data/spec/Features/method_s_span_spec.rb +0 -14
- data/spec/Features/method_seq_spec.rb +0 -47
- data/spec/Features/method_sort_with.rb +0 -43
- data/spec/Features/method_sorted_with.rb +0 -29
- data/spec/Features/method_span_spec.rb +0 -42
- data/spec/Features/method_wrap_readonly_spec.rb +0 -43
- data/spec/UnitTest/test_CAVirtual.rb +0 -214
- data/spec/spec_all.rb +0 -10
- data/utils/ca_ase.rb +0 -21
- data/utils/ca_methods.rb +0 -15
- data/utils/cast_checker.rb +0 -30
- data/utils/convert_test.rb +0 -73
- data/utils/extract_yard.rb +0 -22
- data/utils/guess_shape.rb +0 -76
- data/utils/monkey_patch_methods.rb +0 -62
- data/utils/remove_resource_fork.sh +0 -5
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# CASlabIterator.
|
|
2
|
+
#
|
|
3
|
+
# Created by the C indexer dispatch when an index uses the `:>` slab-axis
|
|
4
|
+
# sigil (e.g. `ca[nil, :>, :>]`). It is a thin sugar over each_slab /
|
|
5
|
+
# map_slab / reduce_slab: `:>` axes become the slab handed to the block;
|
|
6
|
+
# the remaining (sliced) axes are the outer iteration space.
|
|
7
|
+
#
|
|
8
|
+
# ca[nil, :>].each { |row| ... } # = ca.each_slab(axis: 1)
|
|
9
|
+
# ca[2..5, :>, :>].map { |slab| ... } # = ca[2..5,nil,nil].map_slab(axis: [-2,-1])
|
|
10
|
+
#
|
|
11
|
+
# Surface is intentionally minimal (each / map / reduce); to_a / count /
|
|
12
|
+
# size and other Enumerable extras are deferred.
|
|
13
|
+
#
|
|
14
|
+
# Naming note: distinct from the C-defined `CArray::SlabIterator` engine
|
|
15
|
+
# in ext/carray_slab.c -- this is the Ruby-level sigil iterator that
|
|
16
|
+
# delegates to each_slab / map_slab / reduce_slab. Loaded lazily via
|
|
17
|
+
# autoload from lib/carray/autoload/autoload_base.rb on first
|
|
18
|
+
# `ca[..., :>]` evaluation.
|
|
19
|
+
|
|
20
|
+
class CASlabIterator < CAIterator
|
|
21
|
+
# Built from C via `CASlabIterator.new(reference, slab_axes)`: `reference`
|
|
22
|
+
# is the sliced base view (with `:>` axes as full range), `slab_axes` are
|
|
23
|
+
# the positions marked with `:>`.
|
|
24
|
+
def initialize (reference, slab_axes)
|
|
25
|
+
@reference = reference
|
|
26
|
+
@slab_axes = slab_axes
|
|
27
|
+
rdim = reference.shape
|
|
28
|
+
@outer_positions = (0...reference.ndim).reject { |k| slab_axes.include?(k) }.freeze
|
|
29
|
+
@shape = @outer_positions.map { |k| rdim[k] }
|
|
30
|
+
@ndim = @shape.size
|
|
31
|
+
self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The array being iterated (slab exposes it as `reference`; block / window
|
|
35
|
+
# iterators expose theirs as `source`). The base has no common accessor.
|
|
36
|
+
attr_reader :reference, :slab_axes
|
|
37
|
+
|
|
38
|
+
# Slab view at outer index `idx` (Array, length = self.ndim). Returns
|
|
39
|
+
# `@reference[*full_idx]` where slab axes are nil (full range) and
|
|
40
|
+
# outer axes take their values from idx.
|
|
41
|
+
def kernel_at_index (idx)
|
|
42
|
+
full = Array.new(@reference.ndim) # nil at every position
|
|
43
|
+
@outer_positions.each_with_index { |k, i| full[k] = idx[i] }
|
|
44
|
+
@reference[*full]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @overload each { |slab| ... }
|
|
48
|
+
# Yields each slab as an inner CArray. Without a block, returns
|
|
49
|
+
# an `Enumerator` from `each_slab`.
|
|
50
|
+
# @yieldparam slab [CArray]
|
|
51
|
+
# @return [Enumerator, self]
|
|
52
|
+
def each (&block)
|
|
53
|
+
return @reference.each_slab(axis: @slab_axes) unless block
|
|
54
|
+
@reference.each_slab(axis: @slab_axes, &block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @overload map { |slab| ... }
|
|
58
|
+
# Returns a new CArray built by applying the block to each slab,
|
|
59
|
+
# delegating to `map_slab`.
|
|
60
|
+
# @yieldparam slab [CArray]
|
|
61
|
+
# @yieldreturn [Object] per-slab result.
|
|
62
|
+
# @return [CArray]
|
|
63
|
+
def map (&block)
|
|
64
|
+
@reference.map_slab(axis: @slab_axes, &block)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @overload reduce { |acc, slab| ... }
|
|
68
|
+
# Reduces the slabs with `reduce_slab`. The block-only form uses
|
|
69
|
+
# the first slab as the seed; the `init` form starts from `init`.
|
|
70
|
+
# @yieldparam acc [Object] running accumulator.
|
|
71
|
+
# @yieldparam slab [CArray] next slab.
|
|
72
|
+
# @yieldreturn [Object] updated accumulator.
|
|
73
|
+
# @return [Object]
|
|
74
|
+
# @overload reduce(init) { |acc, slab| ... }
|
|
75
|
+
# @param init [Object] initial accumulator value.
|
|
76
|
+
# @yieldparam acc [Object]
|
|
77
|
+
# @yieldparam slab [CArray]
|
|
78
|
+
# @yieldreturn [Object]
|
|
79
|
+
# @return [Object]
|
|
80
|
+
def reduce (*args, &block)
|
|
81
|
+
if args.empty?
|
|
82
|
+
@reference.reduce_slab(axis: @slab_axes, &block)
|
|
83
|
+
else
|
|
84
|
+
@reference.reduce_slab(axis: @slab_axes, init: args[0], &block)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# ---- named reductions -------------------------------------------------
|
|
89
|
+
#
|
|
90
|
+
# A per-slab reduction that folds each slab to one value is exactly the core
|
|
91
|
+
# per-axis reduction over the slab axes, so every reduction is a direct
|
|
92
|
+
# delegation to `reference.<op>(axis: slab_axes)`. This inherits the core
|
|
93
|
+
# dtype, mask, empty/all-masked (identity vs UNDEF) and epsilon-close
|
|
94
|
+
# contracts unchanged -- there is no separate slab reduction kernel. (Unlike
|
|
95
|
+
# the map / reduce block surface, these are mask-aware: they route through the
|
|
96
|
+
# core reduction, which handles masked sources.)
|
|
97
|
+
|
|
98
|
+
# @overload sum
|
|
99
|
+
# Per-slab sum, delegating to `reference.sum(axis: slab_axes)`.
|
|
100
|
+
# @return [CArray] one value per slab (shape = self.dim minus the slab axes)
|
|
101
|
+
# The rest are analogous: prod / mean / min / max, sample and population
|
|
102
|
+
# variance / stddev, all / any, fused minmax ([min, max] pair), the axis-local
|
|
103
|
+
# position min_index / max_index (index within the slab axes), and the flat
|
|
104
|
+
# source address min_addr / max_addr (which source cell holds the extremum).
|
|
105
|
+
[:sum, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
|
|
106
|
+
:variancep, :stddevp, :minmax, :min_index, :max_index,
|
|
107
|
+
:min_addr, :max_addr].each do |op|
|
|
108
|
+
define_method(op) { @reference.send(op, axis: @slab_axes) }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @overload count(v = <none>)
|
|
112
|
+
# Per-slab count, delegating to `reference.count(..., axis: slab_axes)`.
|
|
113
|
+
# No argument counts present (non-masked) cells; `count(UNDEF)` counts
|
|
114
|
+
# masked cells; `count(v)` counts cells equal to `v`.
|
|
115
|
+
# @return [CArray] one count per slab
|
|
116
|
+
def count (*args)
|
|
117
|
+
return count_not_masked if args.empty?
|
|
118
|
+
# CABlock / CAWindow shadow #count with a geometry accessor, and @reference
|
|
119
|
+
# may be such a view, so dispatch CArray#count explicitly for count(v).
|
|
120
|
+
CArray.instance_method(:count).bind_call(@reference, *args, axis: @slab_axes)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @overload count_not_masked
|
|
124
|
+
# Per-slab count of present (non-masked) cells.
|
|
125
|
+
# @return [CArray]
|
|
126
|
+
def count_not_masked
|
|
127
|
+
@reference.count_not_masked(axis: @slab_axes)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @overload count_masked
|
|
131
|
+
# Per-slab count of masked cells.
|
|
132
|
+
# @return [CArray]
|
|
133
|
+
def count_masked
|
|
134
|
+
@reference.count_masked(axis: @slab_axes)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @overload elements
|
|
138
|
+
# Per-slab cell count (structural, mask-independent). Every slab has the
|
|
139
|
+
# same shape, so this is a constant array shaped like the outer iteration
|
|
140
|
+
# space.
|
|
141
|
+
# @return [CArray]
|
|
142
|
+
def elements
|
|
143
|
+
sz = @slab_axes.inject(1) { |p, ax| p * @reference.shape[ax] }
|
|
144
|
+
# count_not_masked gives the correct output shape (and, unlike bare #count,
|
|
145
|
+
# is not shadowed by CABlock / CAWindow); overwrite with the constant size.
|
|
146
|
+
out = @reference.count_not_masked(axis: @slab_axes)
|
|
147
|
+
out[] = sz
|
|
148
|
+
out
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# ---- order statistics (tier 3) ----------------------------------------
|
|
152
|
+
#
|
|
153
|
+
# Core order statistics take a single integer axis, so a single slab axis is
|
|
154
|
+
# delegated directly (fast C path) and a multi-axis slab is folded per slab
|
|
155
|
+
# with reduce_slab (each slab flattened). As with `CArray#median(axis:)`, a
|
|
156
|
+
# masked source raises (the known per-axis masked limitation); strip the mask
|
|
157
|
+
# with `ca.value` first if needed.
|
|
158
|
+
|
|
159
|
+
# @overload median
|
|
160
|
+
# Per-slab median.
|
|
161
|
+
# @return [CArray]
|
|
162
|
+
def median
|
|
163
|
+
if @slab_axes.size == 1
|
|
164
|
+
@reference.median(axis: @slab_axes[0])
|
|
165
|
+
else
|
|
166
|
+
@reference.reduce_slab(axis: @slab_axes) { |s| s.median }
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# @overload percentile(*pers)
|
|
171
|
+
# Per-slab percentile(s). One argument returns one CArray; several return an
|
|
172
|
+
# array of CArrays (as `CArray#percentile`).
|
|
173
|
+
# @return [CArray, Array<CArray>]
|
|
174
|
+
def percentile (*pers)
|
|
175
|
+
if @slab_axes.size == 1
|
|
176
|
+
@reference.percentile(*pers, axis: @slab_axes[0])
|
|
177
|
+
else
|
|
178
|
+
rs = pers.map { |p| @reference.reduce_slab(axis: @slab_axes) { |s| s.percentile(p) } }
|
|
179
|
+
pers.size == 1 ? rs[0] : rs
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @overload quantile
|
|
184
|
+
# Per-slab five-number summary `[min, Q1, median, Q3, max]` (five CArrays),
|
|
185
|
+
# as `CArray#quantile`.
|
|
186
|
+
# @return [Array<CArray>]
|
|
187
|
+
def quantile
|
|
188
|
+
if @slab_axes.size == 1
|
|
189
|
+
@reference.quantile(axis: @slab_axes[0])
|
|
190
|
+
else
|
|
191
|
+
[0, 25, 50, 75, 100].map { |p|
|
|
192
|
+
@reference.reduce_slab(axis: @slab_axes) { |s| s.percentile(p) }
|
|
193
|
+
}
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# ---- order surface ----------------------------------------------------
|
|
198
|
+
#
|
|
199
|
+
# sort_addr / sort_index lift the core order surface per slab. Core sort takes
|
|
200
|
+
# a single axis, so a one-axis slab delegates straight to the C path (unlike
|
|
201
|
+
# the reductions above, sort does not accept an axis array). A multi-axis slab
|
|
202
|
+
# has no single sort axis, so it raises; sort one axis at a time.
|
|
203
|
+
|
|
204
|
+
# @overload sort_addr
|
|
205
|
+
# Per-slab sort by flat source address. Delegates to
|
|
206
|
+
# `reference.sort_addr(axis: slab_axis)`: reference-shaped, each slab's cells
|
|
207
|
+
# carry the flat source addresses that sort that slab ascending.
|
|
208
|
+
# @return [CArray] reference-shaped int64
|
|
209
|
+
def sort_addr
|
|
210
|
+
@reference.sort_addr(axis: single_sort_axis(:sort_addr))
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# @overload sort_index
|
|
214
|
+
# Per-slab sort by axis-local index (usable with take_along_axis).
|
|
215
|
+
# Delegates to `reference.sort_index(axis: slab_axis)`: reference-shaped, the
|
|
216
|
+
# axis-local rank order within the slab axis.
|
|
217
|
+
# @return [CArray] reference-shaped int64
|
|
218
|
+
def sort_index
|
|
219
|
+
@reference.sort_index(axis: single_sort_axis(:sort_index))
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# ---- weighted (tier 2) ------------------------------------------------
|
|
223
|
+
#
|
|
224
|
+
# Delegated to the core weighted reduction over the slab axes; `weights` has
|
|
225
|
+
# the same shape as the reference (one weight per cell). Core wsum / wmean
|
|
226
|
+
# accept multi-axis, so this covers multi-axis slabs directly.
|
|
227
|
+
|
|
228
|
+
# @overload wsum(weights)
|
|
229
|
+
# Per-slab weighted sum, `weights` shaped like the reference.
|
|
230
|
+
# @return [CArray]
|
|
231
|
+
def wsum (weights)
|
|
232
|
+
@reference.wsum(weights, axis: @slab_axes)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# @overload wmean(weights)
|
|
236
|
+
# Per-slab weighted mean, `weights` shaped like the reference.
|
|
237
|
+
# @return [CArray]
|
|
238
|
+
def wmean (weights)
|
|
239
|
+
@reference.wmean(weights, axis: @slab_axes)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# ---- segment scan: within-slab running statistics ---------------------
|
|
243
|
+
#
|
|
244
|
+
# A per-slab running accumulation along the slab axis is exactly the core
|
|
245
|
+
# per-axis cumulative over that axis, so every scan delegates to
|
|
246
|
+
# `reference.<op>(axis: slab_axis)`, inheriting the core dtype / mask (masked
|
|
247
|
+
# cells hold the running total, output unmasked) contracts unchanged. Each
|
|
248
|
+
# cell is in exactly one slab (a partition), so the running value is
|
|
249
|
+
# well-defined; the surface is uniform with the family even though it
|
|
250
|
+
# coincides with a plain per-axis cumulative. Core scan takes a single axis (a
|
|
251
|
+
# multi-axis running accumulation is ambiguous), so a multi-axis slab raises --
|
|
252
|
+
# scan one axis at a time, like the order surface.
|
|
253
|
+
|
|
254
|
+
# @overload cumsum
|
|
255
|
+
# Per-slab inclusive running sum (float64), reference-shaped.
|
|
256
|
+
# @return [CArray]
|
|
257
|
+
# @overload cumprod
|
|
258
|
+
# Per-slab inclusive running product (float64), reference-shaped.
|
|
259
|
+
# @return [CArray]
|
|
260
|
+
# @overload cummax
|
|
261
|
+
# Per-slab inclusive running maximum (reference dtype), reference-shaped.
|
|
262
|
+
# @return [CArray]
|
|
263
|
+
# @overload cummin
|
|
264
|
+
# Per-slab inclusive running minimum (reference dtype), reference-shaped.
|
|
265
|
+
# @return [CArray]
|
|
266
|
+
# @overload cumcount
|
|
267
|
+
# Per-slab running count of present cells (int64), reference-shaped.
|
|
268
|
+
# @return [CArray]
|
|
269
|
+
[:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
|
|
270
|
+
define_method(op) { @reference.send(op, axis: single_scan_axis(op)) }
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
private
|
|
274
|
+
|
|
275
|
+
# The single slab axis for the order surface (core sort takes one integer
|
|
276
|
+
# axis). A multi-axis slab has no single sort axis, so raise.
|
|
277
|
+
def single_sort_axis (op)
|
|
278
|
+
return @slab_axes[0] if @slab_axes.size == 1
|
|
279
|
+
raise ArgumentError,
|
|
280
|
+
"#{op}: a multi-axis slab (axes #{@slab_axes.inspect}) has no single " \
|
|
281
|
+
"sort axis; sort one axis at a time"
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# The single slab axis for a segment scan (core scan takes one integer axis).
|
|
285
|
+
# A multi-axis slab has no single scan axis, so raise.
|
|
286
|
+
def single_scan_axis (op)
|
|
287
|
+
return @slab_axes[0] if @slab_axes.size == 1
|
|
288
|
+
raise ArgumentError,
|
|
289
|
+
"#{op}: a multi-axis slab (axes #{@slab_axes.inspect}) has no single " \
|
|
290
|
+
"scan axis; scan one axis at a time"
|
|
291
|
+
end
|
|
292
|
+
end
|
data/lib/carray/stack.rb
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# CAStack view-default composition surface
|
|
2
|
+
#
|
|
3
|
+
# This file holds the composition methods that build (directly or via
|
|
4
|
+
# reshape/transpose) a CAStack view from storage-uniform pieces:
|
|
5
|
+
#
|
|
6
|
+
# stack = K parents pushed onto a new K axis
|
|
7
|
+
# meld = melt + weld; pieces dissolve their boundaries along the
|
|
8
|
+
# named axis and are regarded as one (uniform)
|
|
9
|
+
# montage = uniform tiles arranged in a regular grid (ImageMagick)
|
|
10
|
+
#
|
|
11
|
+
# Their ragged eager counterparts (concatenate / mosaic / tabulate) live in
|
|
12
|
+
# carray/methods/composition.rb. See that file's header for the full
|
|
13
|
+
# composition cheat sheet.
|
|
14
|
+
#
|
|
15
|
+
# Examples (a, b, c are each shape [3, 4]):
|
|
16
|
+
#
|
|
17
|
+
# CArray.stack([a, b, c]) #=> view, shape (3, 3, 4) (K outermost)
|
|
18
|
+
# CArray.stack([a, b, c], axis: -1) #=> view, shape (3, 4, 3) (RGB pattern)
|
|
19
|
+
# CArray.meld([a, b, c]) #=> view, shape (9, 4)
|
|
20
|
+
# CArray.meld([a, b, c], axis: 1) #=> view, shape (3, 12)
|
|
21
|
+
# CArray.montage([a, b, c, d], [2, 2], axis: 0) #=> view, shape (6, 8) (2x2 image tile)
|
|
22
|
+
#
|
|
23
|
+
# Known limitation: scalar indexing on a CAStack-rooted view drops ndim
|
|
24
|
+
# and triggers a full
|
|
25
|
+
# materialise fallback. Use range indexing (e.g. `meld(...)[k..k, nil]`
|
|
26
|
+
# instead of `meld(...)[k, nil]`) for partial-use perf, or `.to_ca` upfront
|
|
27
|
+
# to materialise eagerly.
|
|
28
|
+
|
|
29
|
+
class CArray
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------- view-default
|
|
32
|
+
|
|
33
|
+
# Stack `list` of CArrays along a new axis inserted at position
|
|
34
|
+
# `axis:` (default 0 = outermost). Returns a view
|
|
35
|
+
# (CAStack with k_axis = axis) when inputs are storage-uniform, or a
|
|
36
|
+
# Face-lifted view (= CATime, CATimedelta, ...) when inputs are
|
|
37
|
+
# homogeneous Face instances. Call `.to_ca` to materialise eagerly.
|
|
38
|
+
#
|
|
39
|
+
# `data_type:` kwarg (optional, primitive Symbol only) forces primitive
|
|
40
|
+
# promotion; cannot be used when the list contains Face elements. Class
|
|
41
|
+
# / Module targets are rejected (= `data_type: CATime` is invalid;
|
|
42
|
+
# use auto-detect for Face round-trip).
|
|
43
|
+
#
|
|
44
|
+
# 3.0 (post-K_AXIS, F.S1-stack landed): replaces `CArray.merge`. The
|
|
45
|
+
# low-level raw constructor is `CAStack.new(list, axis:)`; this method
|
|
46
|
+
# is the high-level surface that performs promote_list + CAStack.new +
|
|
47
|
+
# (face_lift when homogeneous Face).
|
|
48
|
+
# @overload stack(list, axis: 0, data_type: nil)
|
|
49
|
+
# Returns a view stacking uniform-shape arrays along a new K
|
|
50
|
+
# axis at position `axis`. Runs `promote_list` for a common
|
|
51
|
+
# `data_type` and re-wraps homogeneous Face inputs via
|
|
52
|
+
# `face_lift`. Output `ndim` is one greater than each piece.
|
|
53
|
+
# @param list [Array<CArray>] pieces to stack; must not be empty.
|
|
54
|
+
# @param axis [Integer] position of the new K axis.
|
|
55
|
+
# @param data_type [Symbol, Integer, nil] result `data_type`;
|
|
56
|
+
# inferred when `nil`.
|
|
57
|
+
# @return [CArray] CAStack view.
|
|
58
|
+
# @raise [ArgumentError] when `list` is empty.
|
|
59
|
+
def self.stack (list, axis: 0, data_type: nil)
|
|
60
|
+
raise ArgumentError, "stack: list must not be empty" if list.empty?
|
|
61
|
+
list = CArray.promote_list(list, data_type: data_type)
|
|
62
|
+
axis = CArray.normalize_axis(axis, list[0].ndim + 1, "stack")
|
|
63
|
+
CAStack.new(list, axis: axis) # CAStack.new does Face lift internally
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns a {CAMeld} view of the arrays welded along an existing axis.
|
|
67
|
+
# No data is copied; reads gather from parents on demand and writes flow
|
|
68
|
+
# back to them (chain composability preserved).
|
|
69
|
+
#
|
|
70
|
+
# Pieces must agree on `ndim`, `data_type`, byte width, and every axis
|
|
71
|
+
# length except `axis` (the "meld axis"). Mismatched `data_type` raises:
|
|
72
|
+
# cast the pieces yourself (`.to_type(:float64)`) or use
|
|
73
|
+
# {CArray.concatenate} (eager, auto-casts).
|
|
74
|
+
#
|
|
75
|
+
# "meld" = melt + weld — pieces dissolve their boundaries along the named
|
|
76
|
+
# axis and are regarded as one.
|
|
77
|
+
#
|
|
78
|
+
# @overload meld(*arrays, axis: 0)
|
|
79
|
+
# @overload meld(list, axis: 0)
|
|
80
|
+
# Convenience form: a single Array argument is treated as the list.
|
|
81
|
+
# @param arrays [Array<CArray>] pieces to weld. A single Array
|
|
82
|
+
# argument is accepted for compatibility with older callers.
|
|
83
|
+
# @param axis [Integer] existing axis to extend (normalises negative
|
|
84
|
+
# values against the reference ndim).
|
|
85
|
+
# @return [CAMeld] view over the welded pieces.
|
|
86
|
+
# @raise [ArgumentError] when the list is empty, ndim mismatch,
|
|
87
|
+
# data_type mismatch, or non-axis dim mismatch across pieces
|
|
88
|
+
# (surfaced by CAMeld.new).
|
|
89
|
+
def self.meld (*arrays, axis: 0)
|
|
90
|
+
if arrays.length == 1 && arrays[0].is_a?(Array)
|
|
91
|
+
arrays = arrays[0]
|
|
92
|
+
end
|
|
93
|
+
raise ArgumentError, "meld: list must not be empty" if arrays.empty?
|
|
94
|
+
first = arrays[0]
|
|
95
|
+
unless first.is_a?(CArray)
|
|
96
|
+
raise ArgumentError, "meld: entries must be CArray (got #{first.class})"
|
|
97
|
+
end
|
|
98
|
+
axis_norm = CArray.normalize_axis(axis, first.ndim, "meld")
|
|
99
|
+
# Flatten nested CAMeld inputs that share our meld axis: they already
|
|
100
|
+
# describe a segment sequence, so absorbing their parents keeps chain
|
|
101
|
+
# depth at 1 (avoids 2-level xfer_all / reduce chains through the
|
|
102
|
+
# intermediate CAMeld). A CAMeld with a different meld_axis is left
|
|
103
|
+
# intact — its segment structure is orthogonal.
|
|
104
|
+
if arrays.any? { |a| a.is_a?(CAMeld) && a.meld_axis == axis_norm }
|
|
105
|
+
arrays = arrays.flat_map { |a|
|
|
106
|
+
a.is_a?(CAMeld) && a.meld_axis == axis_norm ? a.parents : [a]
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
CAMeld.new(arrays, axis: axis_norm)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class CArray
|
|
114
|
+
# Returns a {CAMeld} view of `[self, *others]` welded along `axis`.
|
|
115
|
+
# Instance form of {CArray.meld}; non-destructive, see the class method
|
|
116
|
+
# for full semantics.
|
|
117
|
+
#
|
|
118
|
+
# @overload meld(*others, axis: 0)
|
|
119
|
+
# @param others [Array<CArray>] additional pieces.
|
|
120
|
+
# @param axis [Integer] existing axis to extend.
|
|
121
|
+
# @return [CAMeld]
|
|
122
|
+
# @raise [ArgumentError] when no `others` are given.
|
|
123
|
+
def meld (*others, axis: 0)
|
|
124
|
+
raise ArgumentError, "meld: at least one other array required" if others.empty?
|
|
125
|
+
CArray.meld(self, *others, axis: axis)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Arrange `list` of uniform-shape pieces in a `tdim`-shape grid that
|
|
129
|
+
# extends parent axes `axis..axis+tdim.size-1` by the corresponding
|
|
130
|
+
# `tdim[i]` factor (= ImageMagick `montage` analog). Output ndim equals
|
|
131
|
+
# each piece's ndim; the tile axes occupy positions
|
|
132
|
+
# `axis..axis+tdim.size-1`. Returns a view; call `.to_ca` to materialise.
|
|
133
|
+
#
|
|
134
|
+
# `tdim.product` must equal `list.size`. For non-uniform pieces along
|
|
135
|
+
# tile axes, use `CArray.mosaic`.
|
|
136
|
+
#
|
|
137
|
+
# Example (parent shape (3, 4), 6-element list, tdim=[2, 3], axis: 0):
|
|
138
|
+
#
|
|
139
|
+
# CArray.montage([a, b, c, d, e, f], [2, 3], axis: 0)
|
|
140
|
+
# #=> shape (6, 12) -- 2 rows x 3 cols grid of (3, 4) blocks
|
|
141
|
+
# # +-----+-----+-----+
|
|
142
|
+
# # | a | b | c | rows 0..2
|
|
143
|
+
# # +-----+-----+-----+
|
|
144
|
+
# # | d | e | f | rows 3..5
|
|
145
|
+
# # +-----+-----+-----+
|
|
146
|
+
#
|
|
147
|
+
# 3.0 (post K_AXIS / promote_list / stack rename): renamed from `combine`
|
|
148
|
+
# (= 20-year vocabulary that didn't describe the action). Positional
|
|
149
|
+
# `at` replaced with `axis:` kwarg for consistency with bind / stack.
|
|
150
|
+
# Parameter order changed from `(tdim, list, at)` to `(list, tdim, axis:)`
|
|
151
|
+
# to align with bind / stack (`list` first).
|
|
152
|
+
# @overload montage(list, tdim, axis: 0, data_type: nil)
|
|
153
|
+
# Returns a view arranging uniform-shape pieces in a `tdim`-shape
|
|
154
|
+
# grid that extends parent axes `axis..axis+tdim.size-1` by the
|
|
155
|
+
# corresponding `tdim[i]` factors. Output `ndim` equals each
|
|
156
|
+
# piece's `ndim`. `tdim.product` must equal `list.size`. For
|
|
157
|
+
# non-uniform pieces along tile axes, use `CArray.mosaic`.
|
|
158
|
+
# @param list [Array<CArray>] pieces to arrange.
|
|
159
|
+
# @param tdim [Array<Integer>] tile grid shape.
|
|
160
|
+
# @param axis [Integer] first tile axis in the result.
|
|
161
|
+
# @param data_type [Symbol, Integer, nil] result `data_type`.
|
|
162
|
+
# @return [CArray] tiled view.
|
|
163
|
+
# @raise [ArgumentError] when `list` is empty or `tdim.product !=
|
|
164
|
+
# list.size`.
|
|
165
|
+
def self.montage (list, tdim, axis: 0, data_type: nil)
|
|
166
|
+
raise ArgumentError, "montage: list must not be empty" if list.empty?
|
|
167
|
+
unless tdim.is_a?(Array) && tdim.size > 0
|
|
168
|
+
raise ArgumentError, "montage: tdim must be a non-empty Array of Integer"
|
|
169
|
+
end
|
|
170
|
+
expected = tdim.inject(1) { |acc, n| acc * n }
|
|
171
|
+
unless expected == list.size
|
|
172
|
+
raise ArgumentError,
|
|
173
|
+
"montage: tdim product (#{expected}) must equal list size (#{list.size})"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
list = CArray.promote_list(list, data_type: data_type)
|
|
177
|
+
parent_shape = list[0].shape
|
|
178
|
+
ntile = tdim.size
|
|
179
|
+
nparent = parent_shape.size
|
|
180
|
+
axis = CArray.normalize_axis(axis, nparent - ntile + 1, "montage")
|
|
181
|
+
|
|
182
|
+
s = CArray.stack(list).reshape(*tdim, *parent_shape) # (K, *) → (*tdim, *)
|
|
183
|
+
|
|
184
|
+
# Interleave: tile axis i (= s axis i, i ∈ [0, ntile)) is moved to
|
|
185
|
+
# just before parent axis (axis + i) in s coordinates (= s axis
|
|
186
|
+
# ntile + axis + i).
|
|
187
|
+
perm = []
|
|
188
|
+
nparent.times do |j|
|
|
189
|
+
if j.between?(axis, axis + ntile - 1)
|
|
190
|
+
perm << (j - axis) # tile axis
|
|
191
|
+
end
|
|
192
|
+
perm << ntile + j # parent axis
|
|
193
|
+
end
|
|
194
|
+
s = s.transpose(*perm)
|
|
195
|
+
|
|
196
|
+
# Merge each (tile[i], parent[axis+i]) pair via reshape.
|
|
197
|
+
new_shape = parent_shape.dup
|
|
198
|
+
ntile.times { |i| new_shape[axis + i] *= tdim[i] }
|
|
199
|
+
s.reshape(*new_shape)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Instance-side stack: build a new K-stack from `[self] + others`
|
|
203
|
+
# along the new K axis at position `axis:`. Always treats `self` as a
|
|
204
|
+
# parent (= even when self is a CAStack, the resulting stack has self
|
|
205
|
+
# as one of its parents, NOT flat-appended into self's parents).
|
|
206
|
+
#
|
|
207
|
+
# For flat-appending into an existing CAStack (= same k_axis, parents
|
|
208
|
+
# extended), use `CAStack#append`.
|
|
209
|
+
#
|
|
210
|
+
# 3.0: high-level Face-aware surface, mirrors `CArray.stack(list, axis:)`.
|
|
211
|
+
# @overload stack(*others, axis: 0, data_type: nil)
|
|
212
|
+
# Returns a K-stack view built from `[self] + others` along a
|
|
213
|
+
# new K axis at position `axis`. Always treats `self` as one
|
|
214
|
+
# parent, even when `self` is a CAStack; use {CAStack#append} to
|
|
215
|
+
# flat-append into an existing CAStack.
|
|
216
|
+
# @param others [Array<CArray>] additional parents.
|
|
217
|
+
# @param axis [Integer] position of the new K axis.
|
|
218
|
+
# @param data_type [Symbol, Integer, nil] result `data_type`.
|
|
219
|
+
# @return [CArray] CAStack view.
|
|
220
|
+
# @raise [ArgumentError] when no `others` are given.
|
|
221
|
+
def stack (*others, axis: 0, data_type: nil)
|
|
222
|
+
raise ArgumentError, "stack: at least one other parent required" if others.empty?
|
|
223
|
+
CArray.stack([self] + others, axis: axis, data_type: data_type)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Split self along a single axis into an Array of (ndim-1)-D slices, each
|
|
227
|
+
# a writable CABlock view. The exact inverse of CArray.stack -- split's
|
|
228
|
+
# slices are all the same shape, so they round-trip back through stack:
|
|
229
|
+
#
|
|
230
|
+
# CArray.stack(a.split(axis: k), axis: k) == a
|
|
231
|
+
#
|
|
232
|
+
# a = CA_INT([[1,2,3], [4,5,6]])
|
|
233
|
+
# a.split(axis: 0) #=> [ <[1,2,3]>, <[4,5,6]> ] (row views)
|
|
234
|
+
# a.split(axis: 1) #=> [ <[1,4]>, <[2,5]>, <[3,6]> ] (column views)
|
|
235
|
+
#
|
|
236
|
+
# 3.0 breaking:
|
|
237
|
+
# - returns a Ruby Array of views (was an object CArray), so it
|
|
238
|
+
# round-trips with CArray.stack (which takes an Array)
|
|
239
|
+
# - +axis:+ takes a single Integer (the multi-axis Array form, which
|
|
240
|
+
# returned an N-D object grid, is no longer accepted)
|
|
241
|
+
# - pieces are CABlock views, NOT copies; writing through a piece
|
|
242
|
+
# mutates +self+. Chain +.copy+ / +.to_ca+ for independent entities.
|
|
243
|
+
# @overload split(axis:)
|
|
244
|
+
# Returns an Array of `(ndim-1)`-D writable CABlock views obtained
|
|
245
|
+
# by splitting `self` along `axis`. Inverse of `CArray.stack`.
|
|
246
|
+
# Slices share storage with `self`; chain `.copy` for independent
|
|
247
|
+
# entities.
|
|
248
|
+
# @param axis [Integer] axis to split along.
|
|
249
|
+
# @return [Array<CArray>] one slice per index along `axis`.
|
|
250
|
+
# @raise [ArgumentError] when `axis` is not a single Integer.
|
|
251
|
+
def split (axis:)
|
|
252
|
+
if axis.is_a?(Array)
|
|
253
|
+
raise ArgumentError, "split: axis must be a single Integer"
|
|
254
|
+
end
|
|
255
|
+
k = normalize_axis(axis, "split")
|
|
256
|
+
(0...shape[k]).map do |i|
|
|
257
|
+
idx = [nil] * ndim
|
|
258
|
+
idx[k] = i
|
|
259
|
+
self[*idx]
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# CAStack.new(list, axis: 0) is implemented in C (ext/ca_obj_stack.c
|
|
266
|
+
# rb_ca_stack_s_new): it overrides Class#new to inspect Face homogeneity
|
|
267
|
+
# and (when applicable) re-wrap the raw CAStack via ca_face_lift. The
|
|
268
|
+
# Ruby class only adds the #append instance method below.
|
|
269
|
+
class CAStack
|
|
270
|
+
# Flat-append `others` into self's parents, preserving the receiver's
|
|
271
|
+
# k_axis. Returns a new CAStack with the combined parents list.
|
|
272
|
+
#
|
|
273
|
+
# Distinct from `CArray#stack(other)` which always treats self as a
|
|
274
|
+
# single parent of a new K-stack -- here, the existing K-stack is
|
|
275
|
+
# extended in place.
|
|
276
|
+
#
|
|
277
|
+
# Example:
|
|
278
|
+
# s = CArray.stack([a, b], axis: 1) # 2-parent stack, k_axis=1
|
|
279
|
+
# s.append(c, d) # 4-parent stack, k_axis=1
|
|
280
|
+
# @overload append(*others)
|
|
281
|
+
# Returns a new CAStack whose parents are `self.parents + others`,
|
|
282
|
+
# preserving `self.k_axis`. Distinct from `CArray#stack`, which
|
|
283
|
+
# always treats `self` as a single parent of a new K-stack.
|
|
284
|
+
# @param others [Array<CArray>] extra parents to append.
|
|
285
|
+
# @return [CAStack]
|
|
286
|
+
# @raise [ArgumentError] when no `others` are given.
|
|
287
|
+
def append (*others)
|
|
288
|
+
raise ArgumentError, "append: at least one parent required" if others.empty?
|
|
289
|
+
CArray.stack(self.parents + others, axis: self.k_axis)
|
|
290
|
+
end
|
|
291
|
+
end
|