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
data/ext/ca_for_buffer.h
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
*
|
|
3
|
+
* ca_for_buffer.h -- sweep WHOLE_BUFFER macro family (whole contig buffer
|
|
4
|
+
* handed to author / third-party library)
|
|
5
|
+
*
|
|
6
|
+
* PROPOSAL_L0_AUTHOR_SURFACE L0.2c (2026-06-11). C-side counterpart of
|
|
7
|
+
* Ruby's `ca.attach! { |a| ... }` block: scope the ca_attach + ca_sync +
|
|
8
|
+
* ca_detach lifecycle to a block, hand the author a contig buffer ptr
|
|
9
|
+
* and element count, let the author do whatever (typically pass the
|
|
10
|
+
* ptr to a third-party library: FFTW, akima init, fitpack surf1, ...).
|
|
11
|
+
*
|
|
12
|
+
* Two forms (Q8 = M1 alias-when-possible; non-contig source materialises
|
|
13
|
+
* into a transient buffer; writable form syncs back on exit):
|
|
14
|
+
*
|
|
15
|
+
* CA_WITH_BUFFER(ca, T, ptr, n)
|
|
16
|
+
* Read-only access. ptr : T const * pointing at native contig
|
|
17
|
+
* layout of `ca` (= ca->ptr alias when contig entity, scratch
|
|
18
|
+
* otherwise). n : ca->elements. No write-back on exit.
|
|
19
|
+
*
|
|
20
|
+
* CA_WITH_BUFFER_WRITABLE(ca, T, ptr, n)
|
|
21
|
+
* Writable; same ptr/n semantics, plus ca_sync on block exit so
|
|
22
|
+
* author writes propagate back to the view's storage.
|
|
23
|
+
*
|
|
24
|
+
* Author pattern:
|
|
25
|
+
*
|
|
26
|
+
* double *ptr;
|
|
27
|
+
* ca_size_t n;
|
|
28
|
+
* CA_WITH_BUFFER_WRITABLE(ca, double, ptr, n) {
|
|
29
|
+
* fftw_execute_dft(plan, ptr, ptr);
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* Constraints (same as CA_FOR_EACH_FIBER family):
|
|
33
|
+
* - `break;` from body exits cleanly (= outer for's advance clause
|
|
34
|
+
* runs ca_sync / ca_detach).
|
|
35
|
+
* - `return;` from body LEAKS the attach; restructure to break.
|
|
36
|
+
* - Macros are NOT statement-equivalent (= nested for); no trailing
|
|
37
|
+
* `else`.
|
|
38
|
+
* - For Ruby-exception-safe lifecycle (e.g. when calling Ruby code or
|
|
39
|
+
* anything that may raise from inside the body), use the function
|
|
40
|
+
* form `rb_ca_call_with_buffer` instead (= rb_ensure-protected).
|
|
41
|
+
*
|
|
42
|
+
* --------------------------------------------------------------------------- */
|
|
43
|
+
|
|
44
|
+
#ifndef CA_FOR_BUFFER_H
|
|
45
|
+
#define CA_FOR_BUFFER_H
|
|
46
|
+
|
|
47
|
+
#include "carray.h"
|
|
48
|
+
#include "ca_sweep_engine.h"
|
|
49
|
+
|
|
50
|
+
/* ---------- macro forms ---------- */
|
|
51
|
+
|
|
52
|
+
/* Read-only: ca_attach (alias-when-possible) + author body + ca_detach. */
|
|
53
|
+
#define CA_WITH_BUFFER(_ca, T, _ptr, _n) \
|
|
54
|
+
for ( CArray *__cwv_ca = (CArray *)(_ca); \
|
|
55
|
+
__cwv_ca; \
|
|
56
|
+
ca_detach(__cwv_ca), __cwv_ca = NULL ) \
|
|
57
|
+
for ( int __cwv_once = (ca_attach(__cwv_ca), \
|
|
58
|
+
(_ptr) = (T *)__cwv_ca->ptr, \
|
|
59
|
+
(_n) = __cwv_ca->elements, \
|
|
60
|
+
1); \
|
|
61
|
+
__cwv_once; \
|
|
62
|
+
__cwv_once = 0 )
|
|
63
|
+
|
|
64
|
+
/* Writable: same as above + ca_sync on block exit. */
|
|
65
|
+
#define CA_WITH_BUFFER_WRITABLE(_ca, T, _ptr, _n) \
|
|
66
|
+
for ( CArray *__cwvw_ca = (CArray *)(_ca); \
|
|
67
|
+
__cwvw_ca; \
|
|
68
|
+
ca_sync(__cwvw_ca), ca_detach(__cwvw_ca), __cwvw_ca = NULL ) \
|
|
69
|
+
for ( int __cwvw_once = (ca_attach(__cwvw_ca), \
|
|
70
|
+
(_ptr) = (T *)__cwvw_ca->ptr, \
|
|
71
|
+
(_n) = __cwvw_ca->elements, \
|
|
72
|
+
1); \
|
|
73
|
+
__cwvw_once; \
|
|
74
|
+
__cwvw_once = 0 )
|
|
75
|
+
|
|
76
|
+
/* ---------- function form (rb_ensure-protected, AC8) ----------
|
|
77
|
+
*
|
|
78
|
+
* Use this when the body may raise a Ruby exception (= calling rb_funcall,
|
|
79
|
+
* type-checking with rb_check_type, indirect Ruby code, etc.). The
|
|
80
|
+
* engine ca_attach's the view, runs body via rb_ensure, then guarantees
|
|
81
|
+
* ca_sync (if writable) and ca_detach run before the exception
|
|
82
|
+
* propagates.
|
|
83
|
+
*
|
|
84
|
+
* body_fn(user_data, ptr, n_elements) -> may raise
|
|
85
|
+
*
|
|
86
|
+
* Returns whatever body_fn returns via its own propagation (= the
|
|
87
|
+
* function itself returns Qnil since rb_ensure body must be VALUE).
|
|
88
|
+
* For richer return semantics, box your result in user_data.
|
|
89
|
+
*/
|
|
90
|
+
typedef void (*ca_with_buffer_body_fn) (void *user_data, void *ptr,
|
|
91
|
+
ca_size_t n_elements);
|
|
92
|
+
|
|
93
|
+
void rb_ca_call_with_buffer (VALUE r_ca, int writable,
|
|
94
|
+
ca_with_buffer_body_fn body, void *user_data);
|
|
95
|
+
|
|
96
|
+
#endif /* CA_FOR_BUFFER_H */
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
*
|
|
3
|
+
* ca_for_each_element.h -- sweep ELEMENT macro family (single-array, lexical
|
|
4
|
+
* scope body) for ext authors.
|
|
5
|
+
*
|
|
6
|
+
* PROPOSAL_L0_AUTHOR_SURFACE L0.2a (2026-06-11). Builds on the sweep engine
|
|
7
|
+
* helper (ca_sweep_engine.{c,h}, L0.1) for the xfer_all-aware acquire/release
|
|
8
|
+
* lifecycle. Provides 5 macro forms for single-array element-wise loops
|
|
9
|
+
* with author-inline body and lexical-scope state capture.
|
|
10
|
+
*
|
|
11
|
+
* Forms (Q2 = gamma: READ by-value, INOUT / OUT by-lvalue):
|
|
12
|
+
*
|
|
13
|
+
* CA_FOR_EACH_ELEMENT(st1, ca, T, x)
|
|
14
|
+
* Read-only loop; per-iter x = cell value (T rvalue convention but
|
|
15
|
+
* declared lvalue for assignment). Mask policy: NO_MASK (raises if
|
|
16
|
+
* INPUT has mask; use _MASKED form to handle).
|
|
17
|
+
*
|
|
18
|
+
* CA_FOR_EACH_ELEMENT_MASKED(st1, ca, T, x, m)
|
|
19
|
+
* Read-only with mask byte m (1 = masked). Author inspects m and
|
|
20
|
+
* decides; m == 0 if source has no mask.
|
|
21
|
+
*
|
|
22
|
+
* CA_FOR_EACH_ELEMENT_INOUT(st2, ca_in, ca_out, T_IN, T_OUT, in, out)
|
|
23
|
+
* Map kernel; per-iter in = read input, body assigns to out (lvalue),
|
|
24
|
+
* out is written back to ca_out. Strict same-shape check on init
|
|
25
|
+
* (raises on mismatch). NO_MASK on input.
|
|
26
|
+
*
|
|
27
|
+
* CA_FOR_EACH_ELEMENT_INOUT_MASKED(st2, ca_in, ca_out, T_IN, T_OUT,
|
|
28
|
+
* in, out, m_in, m_out)
|
|
29
|
+
* Map kernel with mask; per-iter m_in = input mask byte (or 0 if no
|
|
30
|
+
* input mask), author writes m_out (1 = mark output cell masked) and
|
|
31
|
+
* out. m_out written back to ca_out's mask.
|
|
32
|
+
*
|
|
33
|
+
* CA_FOR_EACH_ELEMENT_OUT(st1, ca_out, T, out)
|
|
34
|
+
* Write-only; body assigns to out (lvalue). Use for init / iota /
|
|
35
|
+
* fill patterns. NO_MASK (output mask cleared).
|
|
36
|
+
*
|
|
37
|
+
* Usage pattern:
|
|
38
|
+
*
|
|
39
|
+
* ca_each_state_t st;
|
|
40
|
+
* double x;
|
|
41
|
+
* CA_FOR_EACH_ELEMENT(st, ca, double, x) {
|
|
42
|
+
* accumulator += x;
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* Constraints (same as CA_FOR_EACH_FIBER family):
|
|
46
|
+
* - State (st1 / st2) is author-declared on the stack.
|
|
47
|
+
* - `break;` from body exits cleanly (release runs in outer for's
|
|
48
|
+
* teardown clause).
|
|
49
|
+
* - `return;` from body LEAKS scratch buffers / attached views — do
|
|
50
|
+
* not return early from the macro body; restructure to break.
|
|
51
|
+
* - Macros are NOT statement-equivalent (expand to nested for); no
|
|
52
|
+
* trailing `else`.
|
|
53
|
+
* - `T` must be the data_type of `ca` (matched via ca->data_type and
|
|
54
|
+
* ca->bytes — engine does not auto-cast; use rb_ca_wrap_readonly /
|
|
55
|
+
* rb_ca_template_with_type at the call site if cast needed).
|
|
56
|
+
*
|
|
57
|
+
* --------------------------------------------------------------------------- */
|
|
58
|
+
|
|
59
|
+
#ifndef CA_FOR_EACH_ELEMENT_H
|
|
60
|
+
#define CA_FOR_EACH_ELEMENT_H
|
|
61
|
+
|
|
62
|
+
#include "carray.h"
|
|
63
|
+
#include "ca_sweep_engine.h"
|
|
64
|
+
|
|
65
|
+
/* Stack-allocated state holders for 1- and 2-operand sweep ELEMENT macros.
|
|
66
|
+
* Embeds the per-op bookkeeping arrays + the core ca_sweep_state_t in one
|
|
67
|
+
* struct so authors declare just one variable. */
|
|
68
|
+
|
|
69
|
+
typedef struct {
|
|
70
|
+
CArray *cx[1];
|
|
71
|
+
char *base[1];
|
|
72
|
+
ca_size_t stride[1];
|
|
73
|
+
char *owned_buf[1];
|
|
74
|
+
int attached[1];
|
|
75
|
+
char *base_orig[1];
|
|
76
|
+
ca_sweep_state_t core;
|
|
77
|
+
} ca_each_state_t;
|
|
78
|
+
|
|
79
|
+
typedef struct {
|
|
80
|
+
CArray *cx[2];
|
|
81
|
+
char *base[2];
|
|
82
|
+
ca_size_t stride[2];
|
|
83
|
+
char *owned_buf[2];
|
|
84
|
+
int attached[2];
|
|
85
|
+
char *base_orig[2];
|
|
86
|
+
ca_sweep_state_t core;
|
|
87
|
+
} ca_each_map_state_t;
|
|
88
|
+
|
|
89
|
+
/* Internal init helpers: wire the embedded arrays into core, set fsync /
|
|
90
|
+
* no_mask / src_label. Called via comma-expression inside the macro
|
|
91
|
+
* for-loop init clause. */
|
|
92
|
+
|
|
93
|
+
#define CA_SWEEP_WIRE_1_(_st1, _ca0, _fsync, _no_mask, _label) ( \
|
|
94
|
+
(_st1).cx[0] = (CArray *)(_ca0), \
|
|
95
|
+
(_st1).core.n_ops = 1, \
|
|
96
|
+
(_st1).core.fsync = (_fsync), \
|
|
97
|
+
(_st1).core.cx = (_st1).cx, \
|
|
98
|
+
(_st1).core.base = (_st1).base, \
|
|
99
|
+
(_st1).core.stride = (_st1).stride, \
|
|
100
|
+
(_st1).core.owned_buf = (_st1).owned_buf, \
|
|
101
|
+
(_st1).core.attached = (_st1).attached, \
|
|
102
|
+
(_st1).core.base_orig = (_st1).base_orig, \
|
|
103
|
+
(_st1).core.no_mask = (_no_mask), \
|
|
104
|
+
(_st1).core.src_label = (_label))
|
|
105
|
+
|
|
106
|
+
#define CA_SWEEP_WIRE_2_(_st2, _ca0, _ca1, _fsync, _no_mask, _label) ( \
|
|
107
|
+
(_st2).cx[0] = (CArray *)(_ca0), \
|
|
108
|
+
(_st2).cx[1] = (CArray *)(_ca1), \
|
|
109
|
+
(_st2).core.n_ops = 2, \
|
|
110
|
+
(_st2).core.fsync = (_fsync), \
|
|
111
|
+
(_st2).core.cx = (_st2).cx, \
|
|
112
|
+
(_st2).core.base = (_st2).base, \
|
|
113
|
+
(_st2).core.stride = (_st2).stride, \
|
|
114
|
+
(_st2).core.owned_buf = (_st2).owned_buf, \
|
|
115
|
+
(_st2).core.attached = (_st2).attached, \
|
|
116
|
+
(_st2).core.base_orig = (_st2).base_orig, \
|
|
117
|
+
(_st2).core.no_mask = (_no_mask), \
|
|
118
|
+
(_st2).core.src_label = (_label))
|
|
119
|
+
|
|
120
|
+
/* ---------- 5 author-facing macro forms ----------
|
|
121
|
+
*
|
|
122
|
+
* All 5 forms expand to a 3-level nested for:
|
|
123
|
+
* outer : acquire_chunked / release_chunked (= lifecycle scope)
|
|
124
|
+
* middle : ca_sweep_next_chunk loop (= chunk iteration)
|
|
125
|
+
* inner : per-cell loop within chunk_n
|
|
126
|
+
*
|
|
127
|
+
* Memory peak per AC2: INPUT non-alias views materialise into a single
|
|
128
|
+
* chunk scratch (~32KB at f64). m0 (= masked form) is full size (=
|
|
129
|
+
* n_kernel bytes) for simplicity; macro reads at m0[chunk_off + k].
|
|
130
|
+
*
|
|
131
|
+
* For MASKED forms (m / m_in / m_out): when source has no mask, m0 is
|
|
132
|
+
* NULL and m / m_in == 0 always. m_out writes during INOUT_MASKED are
|
|
133
|
+
* silently dropped if no INPUT mask exists (= AC4 mask-creation on
|
|
134
|
+
* INOUT MASKED with no source mask is a future enhancement; current
|
|
135
|
+
* convention follows the FIBER family's MASKED form).
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
/* (1) READ-only, NO_MASK */
|
|
139
|
+
#define CA_FOR_EACH_ELEMENT(_st1, _ca, T, x) \
|
|
140
|
+
for ( int __cfe_init = (CA_SWEEP_WIRE_1_((_st1), (_ca), "0", 1, \
|
|
141
|
+
"CA_FOR_EACH_ELEMENT"), \
|
|
142
|
+
ca_sweep_acquire_chunked(&(_st1).core), 1); \
|
|
143
|
+
__cfe_init; \
|
|
144
|
+
__cfe_init = 0, ca_sweep_release_chunked(&(_st1).core) ) \
|
|
145
|
+
for ( ; ca_sweep_next_chunk(&(_st1).core); ) \
|
|
146
|
+
for ( ca_size_t __cfe_k = 0; \
|
|
147
|
+
__cfe_k < (_st1).core.chunk_n \
|
|
148
|
+
&& (((x) = *(T *)((_st1).core.base[0] \
|
|
149
|
+
+ __cfe_k * (_st1).core.stride[0])), 1); \
|
|
150
|
+
__cfe_k++ )
|
|
151
|
+
|
|
152
|
+
/* (2) READ-only, MASKED (m is 1 if masked, 0 otherwise; 0 if no source mask) */
|
|
153
|
+
#define CA_FOR_EACH_ELEMENT_MASKED(_st1, _ca, T, x, m) \
|
|
154
|
+
for ( int __cfem_init = (CA_SWEEP_WIRE_1_((_st1), (_ca), "0", 0, \
|
|
155
|
+
"CA_FOR_EACH_ELEMENT_MASKED"), \
|
|
156
|
+
ca_sweep_acquire_chunked(&(_st1).core), 1); \
|
|
157
|
+
__cfem_init; \
|
|
158
|
+
__cfem_init = 0, ca_sweep_release_chunked(&(_st1).core) ) \
|
|
159
|
+
for ( ; ca_sweep_next_chunk(&(_st1).core); ) \
|
|
160
|
+
for ( ca_size_t __cfem_k = 0; \
|
|
161
|
+
__cfem_k < (_st1).core.chunk_n \
|
|
162
|
+
&& (((x) = *(T *)((_st1).core.base[0] \
|
|
163
|
+
+ __cfem_k * (_st1).core.stride[0])), \
|
|
164
|
+
((m) = (_st1).core.m0 \
|
|
165
|
+
? (_st1).core.m0[(_st1).core.chunk_off \
|
|
166
|
+
+ __cfem_k] \
|
|
167
|
+
: (boolean8_t)0), \
|
|
168
|
+
1); \
|
|
169
|
+
__cfem_k++ )
|
|
170
|
+
|
|
171
|
+
/* (3) INOUT map (NO_MASK). Strict same-shape check pre-acquire. */
|
|
172
|
+
#define CA_FOR_EACH_ELEMENT_INOUT(_st2, _ca_in, _ca_out, \
|
|
173
|
+
T_IN, T_OUT, in, out) \
|
|
174
|
+
for ( int __cfei_init = ( \
|
|
175
|
+
ca_sweep_check_same_shape((CArray *)(_ca_in), \
|
|
176
|
+
(CArray *)(_ca_out), \
|
|
177
|
+
"CA_FOR_EACH_ELEMENT_INOUT"), \
|
|
178
|
+
CA_SWEEP_WIRE_2_((_st2), (_ca_in), (_ca_out), "01", 1, \
|
|
179
|
+
"CA_FOR_EACH_ELEMENT_INOUT"), \
|
|
180
|
+
ca_sweep_acquire_chunked(&(_st2).core), \
|
|
181
|
+
1); \
|
|
182
|
+
__cfei_init; \
|
|
183
|
+
__cfei_init = 0, ca_sweep_release_chunked(&(_st2).core) ) \
|
|
184
|
+
for ( ; ca_sweep_next_chunk(&(_st2).core); ) \
|
|
185
|
+
for ( ca_size_t __cfei_k = 0; \
|
|
186
|
+
__cfei_k < (_st2).core.chunk_n \
|
|
187
|
+
&& (((in) = *(T_IN *)((_st2).core.base[0] \
|
|
188
|
+
+ __cfei_k * (_st2).core.stride[0])), \
|
|
189
|
+
1); \
|
|
190
|
+
(*(T_OUT *)((_st2).core.base[1] \
|
|
191
|
+
+ __cfei_k * (_st2).core.stride[1]) = (out)), \
|
|
192
|
+
__cfei_k++ )
|
|
193
|
+
|
|
194
|
+
/* (4) INOUT map (MASKED). Author writes m_out per cell. */
|
|
195
|
+
#define CA_FOR_EACH_ELEMENT_INOUT_MASKED(_st2, _ca_in, _ca_out, \
|
|
196
|
+
T_IN, T_OUT, \
|
|
197
|
+
in, out, m_in, m_out) \
|
|
198
|
+
for ( int __cfeim_init = ( \
|
|
199
|
+
ca_sweep_check_same_shape((CArray *)(_ca_in), \
|
|
200
|
+
(CArray *)(_ca_out), \
|
|
201
|
+
"CA_FOR_EACH_ELEMENT_INOUT_MASKED"), \
|
|
202
|
+
CA_SWEEP_WIRE_2_((_st2), (_ca_in), (_ca_out), "01", 0, \
|
|
203
|
+
"CA_FOR_EACH_ELEMENT_INOUT_MASKED"), \
|
|
204
|
+
ca_sweep_acquire_chunked(&(_st2).core), \
|
|
205
|
+
1); \
|
|
206
|
+
__cfeim_init; \
|
|
207
|
+
__cfeim_init = 0, ca_sweep_release_chunked(&(_st2).core) ) \
|
|
208
|
+
for ( ; ca_sweep_next_chunk(&(_st2).core); ) \
|
|
209
|
+
for ( ca_size_t __cfeim_k = 0; \
|
|
210
|
+
__cfeim_k < (_st2).core.chunk_n \
|
|
211
|
+
&& (((in) = *(T_IN *)((_st2).core.base[0] \
|
|
212
|
+
+ __cfeim_k * (_st2).core.stride[0])), \
|
|
213
|
+
((m_in) = (_st2).core.m0 \
|
|
214
|
+
? (_st2).core.m0[(_st2).core.chunk_off \
|
|
215
|
+
+ __cfeim_k] \
|
|
216
|
+
: (boolean8_t)0), \
|
|
217
|
+
((m_out) = (m_in)), \
|
|
218
|
+
1); \
|
|
219
|
+
(*(T_OUT *)((_st2).core.base[1] \
|
|
220
|
+
+ __cfeim_k * (_st2).core.stride[1]) = (out)), \
|
|
221
|
+
(((_st2).core.m0) \
|
|
222
|
+
? ((_st2).core.m0[(_st2).core.chunk_off + __cfeim_k] \
|
|
223
|
+
= (m_out)) \
|
|
224
|
+
: (boolean8_t)0), \
|
|
225
|
+
__cfeim_k++ )
|
|
226
|
+
|
|
227
|
+
/* (5) WRITE-only (init / iota / fill). */
|
|
228
|
+
#define CA_FOR_EACH_ELEMENT_OUT(_st1, _ca_out, T, out) \
|
|
229
|
+
for ( int __cfeo_init = (CA_SWEEP_WIRE_1_((_st1), (_ca_out), "1", 0, \
|
|
230
|
+
"CA_FOR_EACH_ELEMENT_OUT"), \
|
|
231
|
+
ca_sweep_acquire_chunked(&(_st1).core), 1); \
|
|
232
|
+
__cfeo_init; \
|
|
233
|
+
__cfeo_init = 0, ca_sweep_release_chunked(&(_st1).core) ) \
|
|
234
|
+
for ( ; ca_sweep_next_chunk(&(_st1).core); ) \
|
|
235
|
+
for ( ca_size_t __cfeo_k = 0; \
|
|
236
|
+
__cfeo_k < (_st1).core.chunk_n; \
|
|
237
|
+
(*(T *)((_st1).core.base[0] \
|
|
238
|
+
+ __cfeo_k * (_st1).core.stride[0]) = (out)), \
|
|
239
|
+
__cfeo_k++ )
|
|
240
|
+
|
|
241
|
+
#endif /* CA_FOR_EACH_ELEMENT_H */
|
data/ext/ca_group_iter.c
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
Axis-group reduction surface: the `[]` type gate and CAGroupIterator.
|
|
4
|
+
|
|
5
|
+
The apply path is C-level: the `[]` type gate that routes a CACategorical /
|
|
6
|
+
AxisGroup index to the group path (vs ordinary selection), the
|
|
7
|
+
CAGroupIterator construction and the :group reduce dispatch all live here.
|
|
8
|
+
The classifier is CACategorical (`categorical` (lib/carray/categorical.rb)):
|
|
9
|
+
a READONLY Face over narrow-uint codes; this surface consumes its codes / k
|
|
10
|
+
/ labels. The value-independent metadata (AxisGroup spec derivation,
|
|
11
|
+
GroupLabels) is Ruby (`axis_group` (lib/carray/axis_group.rb)) — it is not on
|
|
12
|
+
the `[]` hot path and is plain O(ndim) metadata.
|
|
13
|
+
|
|
14
|
+
Split summary:
|
|
15
|
+
- ca_argv_has_group / rb_ca_fetch_group : here (called by carray_access.c)
|
|
16
|
+
- CAGroupIterator + the tier-1 reduction methods driving
|
|
17
|
+
__axis_group_reduce__ : here
|
|
18
|
+
- reduce_plan / bundle assembly / output-axis permutation (metadata) : Ruby
|
|
19
|
+
- the O(N) compute kernel __axis_group_reduce__ : ca_axis_group.c
|
|
20
|
+
|
|
21
|
+
--------------------------------------------------------------------------- */
|
|
22
|
+
|
|
23
|
+
#include "carray.h"
|
|
24
|
+
|
|
25
|
+
/* lib/carray/axis_group.rb registers these classes here on first load (and
|
|
26
|
+
loading is wired so that load happens as soon as any categorical exists —
|
|
27
|
+
see autoload_base.rb). Until then they stay Qnil and the [] type-gate scan
|
|
28
|
+
short-circuits, so a normal index pays nothing (no kind_of on the hot
|
|
29
|
+
path). */
|
|
30
|
+
static VALUE rb_cCategorical = Qnil;
|
|
31
|
+
static VALUE rb_cAxisGroup = Qnil;
|
|
32
|
+
static VALUE rb_cCAGroupIterator = Qnil;
|
|
33
|
+
|
|
34
|
+
static ID id_value, id_spec, id_axis_group, id_reduce_plan, id_parse_axis;
|
|
35
|
+
static ID id_axis_group_reduce, id_reshape, id_transpose, id_labels;
|
|
36
|
+
|
|
37
|
+
/* CArray.__register_axis_group_classes__(CACategorical, AxisGroup) — called
|
|
38
|
+
from the top of lib/carray/axis_group.rb so the C type gate can recognise
|
|
39
|
+
the classifier/spec types without triggering autoload on every [] call. */
|
|
40
|
+
static VALUE
|
|
41
|
+
rb_ca_register_axis_group_classes (VALUE klass, VALUE cat, VALUE ag)
|
|
42
|
+
{
|
|
43
|
+
rb_cCategorical = cat;
|
|
44
|
+
rb_cAxisGroup = ag;
|
|
45
|
+
rb_gc_register_address(&rb_cCategorical);
|
|
46
|
+
rb_gc_register_address(&rb_cAxisGroup);
|
|
47
|
+
return Qnil;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Cheap per-arg pre-filter + kind_of check. Returns 1 if any arg is a
|
|
51
|
+
CACategorical (one-shot slot) or an AxisGroup (pre-built spec) — the group
|
|
52
|
+
apply path — and 0 otherwise.
|
|
53
|
+
|
|
54
|
+
The two group-arg forms have different Ruby representations, so each gets a
|
|
55
|
+
pre-filter that keeps ordinary indexing off the kind_of walk:
|
|
56
|
+
|
|
57
|
+
- AxisGroup is a plain Ruby object (T_OBJECT). The scalar index kinds
|
|
58
|
+
(Integer / nil / Symbol / Range) are not T_OBJECT, so they cost only the
|
|
59
|
+
RB_TYPE_P bit test.
|
|
60
|
+
|
|
61
|
+
- CACategorical is a CArray (T_DATA) whose SURFACE data_type is CA_FIXLEN
|
|
62
|
+
(its narrow-uint codes are the storage parent). A plain fancy-index is a
|
|
63
|
+
numeric CArray, so the cheap data_type read lets ordinary selection args
|
|
64
|
+
skip the kind_of — only a fixlen CArray index pays it.
|
|
65
|
+
|
|
66
|
+
Arming: the classes are registered by lib/carray/axis_group.rb. We cannot
|
|
67
|
+
pre-arm on categorical construction (CACategorical is a CArray, so routing
|
|
68
|
+
its autoload through the axis-group file recurses when categorical.rb reopens
|
|
69
|
+
the class), so the gate arms itself lazily the first time it sees a fixlen
|
|
70
|
+
CArray index — the only thing a categorical can be, and never a real plain
|
|
71
|
+
index. That covers a categorical built via CACategorical.from_codes that
|
|
72
|
+
never touched the axis-group file. Scalar / range / integer indices never
|
|
73
|
+
reach this branch, so they never trigger the require. */
|
|
74
|
+
int
|
|
75
|
+
ca_argv_has_group (int argc, VALUE *argv)
|
|
76
|
+
{
|
|
77
|
+
int i;
|
|
78
|
+
for ( i = 0; i < argc; i++ ) {
|
|
79
|
+
VALUE a = argv[i];
|
|
80
|
+
if ( RB_TYPE_P(a, T_OBJECT) ) {
|
|
81
|
+
if ( ! NIL_P(rb_cAxisGroup) && rb_obj_is_kind_of(a, rb_cAxisGroup) ) {
|
|
82
|
+
return 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if ( RB_TYPE_P(a, T_DATA)
|
|
86
|
+
&& rb_typeddata_is_kind_of(a, &carray_data_type) ) {
|
|
87
|
+
CArray *ca = (CArray *) DATA_PTR(a);
|
|
88
|
+
if ( ca->data_type == CA_FIXLEN ) {
|
|
89
|
+
if ( NIL_P(rb_cCategorical) ) {
|
|
90
|
+
rb_require("carray/axis_group"); /* lazily arm (registers) */
|
|
91
|
+
}
|
|
92
|
+
if ( ! NIL_P(rb_cCategorical) && rb_obj_is_kind_of(a, rb_cCategorical) ) {
|
|
93
|
+
return 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Build a CAGroupIterator for `self[*argv]`. Two entry forms:
|
|
102
|
+
(A) one-shot self[idx, nil, ...] -> build the spec via axis_group(*argv)
|
|
103
|
+
(B) pre-built self[g] -> g is already an AxisGroup spec
|
|
104
|
+
Pre-condition: ca_argv_has_group(argc, argv) is true. */
|
|
105
|
+
VALUE
|
|
106
|
+
rb_ca_fetch_group (int argc, VALUE *argv, VALUE self)
|
|
107
|
+
{
|
|
108
|
+
volatile VALUE spec;
|
|
109
|
+
|
|
110
|
+
if ( argc == 1 && ! NIL_P(rb_cAxisGroup)
|
|
111
|
+
&& rb_obj_is_kind_of(argv[0], rb_cAxisGroup) ) {
|
|
112
|
+
spec = argv[0];
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
/* one-shot: raw CACategorical / nil slots -> build the spec (axis_group
|
|
116
|
+
validates the slot layout = one slot per source axis). */
|
|
117
|
+
spec = rb_funcallv(self, id_axis_group, argc, argv);
|
|
118
|
+
}
|
|
119
|
+
return rb_funcall(rb_cCAGroupIterator, rb_intern("__build__"), 2, self, spec);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* --- CAGroupIterator -------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
A reduction dispatcher, not an array: it holds the source value + the
|
|
125
|
+
AxisGroup spec and exposes the tier-1 reductions. There is no to_ca / copy
|
|
126
|
+
/ materialise path — the grouping only exists as the reduction's argument. */
|
|
127
|
+
|
|
128
|
+
static VALUE
|
|
129
|
+
rb_ca_group_iter_build (VALUE klass, VALUE value, VALUE spec)
|
|
130
|
+
{
|
|
131
|
+
VALUE obj = rb_obj_alloc(klass);
|
|
132
|
+
rb_ivar_set(obj, id_value, value);
|
|
133
|
+
rb_ivar_set(obj, id_spec, spec);
|
|
134
|
+
return obj;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static VALUE
|
|
138
|
+
rb_ca_group_iter_value (VALUE self)
|
|
139
|
+
{
|
|
140
|
+
return rb_ivar_get(self, id_value);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static VALUE
|
|
144
|
+
rb_ca_group_iter_spec (VALUE self)
|
|
145
|
+
{
|
|
146
|
+
return rb_ivar_get(self, id_spec);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
static VALUE
|
|
150
|
+
rb_ca_group_iter_labels (int argc, VALUE *argv, VALUE self)
|
|
151
|
+
{
|
|
152
|
+
VALUE spec = rb_ivar_get(self, id_spec);
|
|
153
|
+
return rb_funcallv_kw(spec, id_labels, argc, argv, RB_PASS_CALLED_KEYWORDS);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Reduce the grouped result by `perm`/`squeeze` from the plan:
|
|
157
|
+
result (= [K_total, *band]) -> reshape [*group_dims, *band]
|
|
158
|
+
-> transpose to slot order (perm)
|
|
159
|
+
-> squeeze the length-1 fused-band axes. */
|
|
160
|
+
static VALUE
|
|
161
|
+
group_iter_shape_output (VALUE result, VALUE group_dims, VALUE perm, VALUE squeeze)
|
|
162
|
+
{
|
|
163
|
+
CArray *cr;
|
|
164
|
+
VALUE dims[CA_RANK_MAX];
|
|
165
|
+
VALUE pargv[CA_RANK_MAX];
|
|
166
|
+
long ng, nband, nd, i;
|
|
167
|
+
|
|
168
|
+
GetCArray(result, cr);
|
|
169
|
+
|
|
170
|
+
/* reshape target = group_dims ++ result.shape[1..] (drop leading K_total) */
|
|
171
|
+
ng = RARRAY_LEN(group_dims);
|
|
172
|
+
nband = cr->ndim - 1;
|
|
173
|
+
nd = ng + nband;
|
|
174
|
+
if ( nd > CA_RANK_MAX ) {
|
|
175
|
+
rb_raise(rb_eRuntimeError, "axis_group: output ndim %ld too large", nd);
|
|
176
|
+
}
|
|
177
|
+
for ( i = 0; i < ng; i++ ) {
|
|
178
|
+
dims[i] = RARRAY_AREF(group_dims, i);
|
|
179
|
+
}
|
|
180
|
+
for ( i = 0; i < nband; i++ ) {
|
|
181
|
+
dims[ng + i] = SIZE2NUM(cr->dim[i + 1]);
|
|
182
|
+
}
|
|
183
|
+
result = rb_funcall2(result, id_reshape, (int) nd, dims);
|
|
184
|
+
|
|
185
|
+
/* transpose to slot order */
|
|
186
|
+
for ( i = 0; i < nd; i++ ) {
|
|
187
|
+
pargv[i] = RARRAY_AREF(perm, i);
|
|
188
|
+
}
|
|
189
|
+
result = rb_funcall2(result, id_transpose, (int) nd, pargv);
|
|
190
|
+
|
|
191
|
+
/* squeeze the fused (length-1) band slots */
|
|
192
|
+
if ( RARRAY_LEN(squeeze) > 0 ) {
|
|
193
|
+
char drop[CA_RANK_MAX];
|
|
194
|
+
VALUE keep[CA_RANK_MAX];
|
|
195
|
+
long nkeep = 0, j;
|
|
196
|
+
GetCArray(result, cr);
|
|
197
|
+
for ( i = 0; i < cr->ndim; i++ ) drop[i] = 0;
|
|
198
|
+
for ( j = 0; j < RARRAY_LEN(squeeze); j++ ) {
|
|
199
|
+
long s = NUM2LONG(RARRAY_AREF(squeeze, j));
|
|
200
|
+
if ( s >= 0 && s < cr->ndim ) drop[s] = 1;
|
|
201
|
+
}
|
|
202
|
+
for ( i = 0; i < cr->ndim; i++ ) {
|
|
203
|
+
if ( ! drop[i] ) keep[nkeep++] = SIZE2NUM(cr->dim[i]);
|
|
204
|
+
}
|
|
205
|
+
result = rb_funcall2(result, id_reshape, (int) nkeep, keep);
|
|
206
|
+
}
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Shared driver for every tier-1 reduction. The op is read from the called
|
|
211
|
+
method name (rb_frame_this_func), so the 10 white-list names all bind here.
|
|
212
|
+
axis: absent / no :group -> delegate to the value's plain same-named
|
|
213
|
+
reduction (grouping NOT engaged). axis: contains :group -> group reduction
|
|
214
|
+
(+ any integer band axes folded into the statistic). */
|
|
215
|
+
static VALUE
|
|
216
|
+
group_iter_reduce (int argc, VALUE *argv, VALUE self)
|
|
217
|
+
{
|
|
218
|
+
ID op_id = rb_frame_this_func();
|
|
219
|
+
VALUE op_sym = ID2SYM(op_id);
|
|
220
|
+
VALUE value = rb_ivar_get(self, id_value);
|
|
221
|
+
VALUE spec = rb_ivar_get(self, id_spec);
|
|
222
|
+
VALUE kw = Qnil, vaxis;
|
|
223
|
+
VALUE parsed, plan;
|
|
224
|
+
VALUE group_axes, bundles, group_dims, perm, squeeze, fused;
|
|
225
|
+
VALUE result;
|
|
226
|
+
|
|
227
|
+
rb_scan_args(argc, argv, "0:", &kw);
|
|
228
|
+
vaxis = NIL_P(kw)
|
|
229
|
+
? Qundef
|
|
230
|
+
: rb_hash_lookup2(kw, ID2SYM(rb_intern("axis")), Qundef);
|
|
231
|
+
|
|
232
|
+
/* (has_group, fused_band_slots) */
|
|
233
|
+
parsed = rb_funcall(rb_cAxisGroup, id_parse_axis,
|
|
234
|
+
1, (vaxis == Qundef) ? Qnil : vaxis);
|
|
235
|
+
if ( RTEST(RARRAY_AREF(parsed, 0)) == 0 ) {
|
|
236
|
+
/* no :group -> plain reduction on the value (grouping not engaged) */
|
|
237
|
+
if ( vaxis == Qundef ) {
|
|
238
|
+
return rb_funcall(value, op_id, 0);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
VALUE h = rb_hash_new();
|
|
242
|
+
rb_hash_aset(h, ID2SYM(rb_intern("axis")), vaxis);
|
|
243
|
+
return rb_funcallv_kw(value, op_id, 1, &h, RB_PASS_KEYWORDS);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* group reduction */
|
|
248
|
+
fused = RARRAY_AREF(parsed, 1);
|
|
249
|
+
plan = rb_funcall(spec, id_reduce_plan, 1, fused);
|
|
250
|
+
group_axes = RARRAY_AREF(plan, 0);
|
|
251
|
+
bundles = RARRAY_AREF(plan, 1);
|
|
252
|
+
group_dims = RARRAY_AREF(plan, 2);
|
|
253
|
+
perm = RARRAY_AREF(plan, 3);
|
|
254
|
+
squeeze = RARRAY_AREF(plan, 4);
|
|
255
|
+
|
|
256
|
+
result = rb_funcall(value, id_axis_group_reduce, 3,
|
|
257
|
+
group_axes, bundles, op_sym);
|
|
258
|
+
return group_iter_shape_output(result, group_dims, perm, squeeze);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
void
|
|
262
|
+
Init_ca_group_iter (void)
|
|
263
|
+
{
|
|
264
|
+
id_value = rb_intern("@value");
|
|
265
|
+
id_spec = rb_intern("@spec");
|
|
266
|
+
id_axis_group = rb_intern("axis_group");
|
|
267
|
+
id_reduce_plan = rb_intern("reduce_plan");
|
|
268
|
+
id_parse_axis = rb_intern("parse_axis");
|
|
269
|
+
id_axis_group_reduce = rb_intern("__axis_group_reduce__");
|
|
270
|
+
id_reshape = rb_intern("reshape");
|
|
271
|
+
id_transpose = rb_intern("transpose");
|
|
272
|
+
id_labels = rb_intern("labels");
|
|
273
|
+
|
|
274
|
+
rb_define_singleton_method(rb_cCArray, "__register_axis_group_classes__",
|
|
275
|
+
rb_ca_register_axis_group_classes, 2);
|
|
276
|
+
|
|
277
|
+
rb_cCAGroupIterator =
|
|
278
|
+
rb_define_class("CAGroupIterator",
|
|
279
|
+
rb_const_get(rb_cObject, rb_intern("CAIterator")));
|
|
280
|
+
rb_gc_register_address(&rb_cCAGroupIterator);
|
|
281
|
+
|
|
282
|
+
rb_define_singleton_method(rb_cCAGroupIterator, "__build__",
|
|
283
|
+
rb_ca_group_iter_build, 2);
|
|
284
|
+
rb_define_method(rb_cCAGroupIterator, "value", rb_ca_group_iter_value, 0);
|
|
285
|
+
rb_define_method(rb_cCAGroupIterator, "spec", rb_ca_group_iter_spec, 0);
|
|
286
|
+
rb_define_method(rb_cCAGroupIterator, "labels", rb_ca_group_iter_labels, -1);
|
|
287
|
+
|
|
288
|
+
/* reduction white-list. All bind to one driver; the op is recovered from
|
|
289
|
+
the called method name. count_not_masked is a synonym of count (present
|
|
290
|
+
count); elements / count_masked / minmax / wsum / wmean are composed in
|
|
291
|
+
Ruby (lib/carray/axis_group.rb). min_addr / max_addr give the flat source
|
|
292
|
+
address of the extremum (there is no group-local min_index for the group
|
|
293
|
+
iterator -- the order-preserving flat address is the meaningful position). */
|
|
294
|
+
{
|
|
295
|
+
const char *ops[] = { "sum", "prod", "mean", "min", "max",
|
|
296
|
+
"variance", "stddev", "variancep", "stddevp",
|
|
297
|
+
"count", "count_not_masked", "min_addr", "max_addr",
|
|
298
|
+
"all", "any", NULL };
|
|
299
|
+
int i;
|
|
300
|
+
for ( i = 0; ops[i]; i++ ) {
|
|
301
|
+
rb_define_method(rb_cCAGroupIterator, ops[i], group_iter_reduce, -1);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|