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_obj_monop.c
ADDED
|
@@ -0,0 +1,1111 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
Lazy monadic element-wise op view (CAMonOp). Holds (parent, op_id,
|
|
4
|
+
output_data_type). Carries `CA_FLAG_READ_ONLY`; `[]=` raises, except
|
|
5
|
+
the writable cast / byte_swap op_ids.
|
|
6
|
+
|
|
7
|
+
## In-place chain evaluation
|
|
8
|
+
|
|
9
|
+
When materialising a chain `a.lazy.f.g.h.to_ca`, the xfer_stride does:
|
|
10
|
+
|
|
11
|
+
1. iterative chain collect: walk this->parent->...->parent until a
|
|
12
|
+
non-CAMonOp leaf is found, recording op_ids bottom-up
|
|
13
|
+
2. pull leaf into the output buffer (= `data`) via ca_xfer_stride —
|
|
14
|
+
or, when a cast node is at the innermost position (cast-before),
|
|
15
|
+
pull into a small leaf-data_type scratch and ca_cast_block into output
|
|
16
|
+
3. apply remaining ops in reverse order, in-place on `data` (=
|
|
17
|
+
ptr1==ptr2==data, i1==i2==1)
|
|
18
|
+
|
|
19
|
+
Chain-intermediate scratch count = 0, depth-independent. At most one
|
|
20
|
+
leaf-scratch (for the cast input) when a widening cast is required;
|
|
21
|
+
this is also depth-independent. monop is element-local, so in-place
|
|
22
|
+
is safe; masked cells may be computed but are unobservable (mask
|
|
23
|
+
propagates on a separate channel).
|
|
24
|
+
|
|
25
|
+
## Scope
|
|
26
|
+
|
|
27
|
+
- 34 ops via dispatch table (`ext/ca_monop_dispatch.c`)
|
|
28
|
+
- Cast nodes for widening monfunc on an integer parent (cast-before)
|
|
29
|
+
- Per-(op, data_type) byte parity with the eager path
|
|
30
|
+
|
|
31
|
+
---------------------------------------------------------------------------- */
|
|
32
|
+
|
|
33
|
+
#include "carray.h"
|
|
34
|
+
#include "carray_internal.h" /* ca_lazy_arena_*, ca_is_lazy_view */
|
|
35
|
+
#include "ca_monop_dispatch.h"
|
|
36
|
+
|
|
37
|
+
/* ca_cast_block (carray_cast.c) */
|
|
38
|
+
extern void ca_cast_block (ca_size_t n, void *ap1, void *ptr1,
|
|
39
|
+
void *ap2, void *ptr2);
|
|
40
|
+
|
|
41
|
+
/* byte_swap buffer-level helper (ext/ca_obj_byte_swap.c). Handles CMPLX
|
|
42
|
+
half-independent swap (CMPLX64 = 4-byte halves, CMPLX128 = 8-byte
|
|
43
|
+
halves); other data_types = ca_swap_bytes(buf, bytes, elements).
|
|
44
|
+
CA_FIXLEN+data_class is excluded. */
|
|
45
|
+
extern void ca_byte_swap_buffer (int8_t data_type, ca_size_t bytes,
|
|
46
|
+
ca_size_t elements, char *buf);
|
|
47
|
+
|
|
48
|
+
/* ------------------------------------------------------------------- */
|
|
49
|
+
/* CAMonOp struct */
|
|
50
|
+
/* ------------------------------------------------------------------- */
|
|
51
|
+
|
|
52
|
+
int8_t CA_OBJ_MONOP;
|
|
53
|
+
VALUE rb_cCAMonOp;
|
|
54
|
+
|
|
55
|
+
/* Defined in ext/carray_lazy.c — needed for collapse-on-consume and
|
|
56
|
+
chain-walk leaf detection. */
|
|
57
|
+
extern int8_t CA_OBJ_LAZY_MARKER;
|
|
58
|
+
|
|
59
|
+
/* CABinOp / CABinCmp / CAMonCmp obj_type forward decls (defined in
|
|
60
|
+
ext/ca_obj_*.c). Used by ca_monop_view_is_single_cast to recognise
|
|
61
|
+
lazy parents. (Re-externed at the end of this file; a duplicate decl
|
|
62
|
+
is safe.) */
|
|
63
|
+
extern int8_t CA_OBJ_BINOP;
|
|
64
|
+
extern int8_t CA_OBJ_BINCMP;
|
|
65
|
+
extern int8_t CA_OBJ_MONCMP;
|
|
66
|
+
|
|
67
|
+
typedef struct CAMonOp {
|
|
68
|
+
int16_t obj_type;
|
|
69
|
+
int8_t data_type;
|
|
70
|
+
int8_t ndim;
|
|
71
|
+
int32_t flags;
|
|
72
|
+
ca_size_t bytes;
|
|
73
|
+
ca_size_t elements;
|
|
74
|
+
ca_size_t *dim;
|
|
75
|
+
char *ptr;
|
|
76
|
+
CArray *mask;
|
|
77
|
+
char *_pool; /* framework-managed pool buffer (NULL = legacy ALLOC_N path). */
|
|
78
|
+
CArray *parent;
|
|
79
|
+
uint32_t attach;
|
|
80
|
+
uint8_t nosync;
|
|
81
|
+
/* CAMonOp-specific tail */
|
|
82
|
+
uint16_t op_id;
|
|
83
|
+
/* data_class snapshot for cast op_id paths that need data_class
|
|
84
|
+
metadata propagation (e.g. `as_type(MyStruct)` CA_FIXLEN + data_class
|
|
85
|
+
field-recursive operations). Stays Qnil for the non-cast or
|
|
86
|
+
no-data_class case; mirrors the CAByteSwap pattern. */
|
|
87
|
+
VALUE data_class;
|
|
88
|
+
} CAMonOp;
|
|
89
|
+
|
|
90
|
+
static size_t
|
|
91
|
+
ca_monop_dsize (const void *ap)
|
|
92
|
+
{
|
|
93
|
+
const CAMonOp *ca = (const CAMonOp *) ap;
|
|
94
|
+
return sizeof(CAMonOp) + ca->ndim * sizeof(ca_size_t);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Pool framework hooks: single ndim-sized tail (dim) in the _pool buffer
|
|
98
|
+
(uniform alloc/free discipline). */
|
|
99
|
+
static size_t
|
|
100
|
+
ca_monop_pool_bytes (int8_t ndim)
|
|
101
|
+
{
|
|
102
|
+
ca_size_t n = (ndim > 0) ? ndim : 1;
|
|
103
|
+
return (size_t) n * sizeof(ca_size_t);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static void
|
|
107
|
+
ca_monop_pool_init (void *ap, int8_t ndim)
|
|
108
|
+
{
|
|
109
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
110
|
+
ca->dim = (ca_size_t *) ca->_pool;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Custom dmark: mark the prefix (= parent ivar + mask + standard CArray
|
|
114
|
+
VALUE refs) AND the tail data_class VALUE. Mirrors ca_byte_swap_mark. */
|
|
115
|
+
static void
|
|
116
|
+
ca_monop_mark (void *ap)
|
|
117
|
+
{
|
|
118
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
119
|
+
ca_mark(ca); /* prefix */
|
|
120
|
+
rb_gc_mark(ca->data_class); /* tail VALUE */
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const rb_data_type_t camonop_data_type = {
|
|
124
|
+
.parent = &caview_data_type,
|
|
125
|
+
.wrap_struct_name = "CAMonOp",
|
|
126
|
+
.function = {
|
|
127
|
+
.dmark = ca_monop_mark,
|
|
128
|
+
.dfree = ca_free,
|
|
129
|
+
.dsize = ca_monop_dsize,
|
|
130
|
+
.dcompact = NULL
|
|
131
|
+
},
|
|
132
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/* ------------------------------------------------------------------- */
|
|
136
|
+
/* single-cast discrimination helper */
|
|
137
|
+
/* ------------------------------------------------------------------- */
|
|
138
|
+
|
|
139
|
+
/* See ca_monop_dispatch.h for contract. Returns 1 iff `view` is a
|
|
140
|
+
CAMonOp instance whose op_id is a cast op AND whose parent is not also
|
|
141
|
+
a CAMonOp (= single-node cast view, the structural successor of CAFake).
|
|
142
|
+
This is the discrimination predicate used by ca_kernel_iterator's
|
|
143
|
+
per-fiber fused gate to keep the transform-fused fast path live for
|
|
144
|
+
single-cast views while chain CAMonOp (= depth >= 2) continues through
|
|
145
|
+
the chain materialise path. */
|
|
146
|
+
int
|
|
147
|
+
ca_monop_view_is_single_cast (CArray *view)
|
|
148
|
+
{
|
|
149
|
+
CAMonOp *m;
|
|
150
|
+
CArray *p;
|
|
151
|
+
if ( view == NULL || view->obj_type != CA_OBJ_MONOP ) {
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
m = (CAMonOp *) view;
|
|
155
|
+
if ( ! ca_monop_is_cast(m->op_id) ) {
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
p = m->parent;
|
|
159
|
+
if ( p == NULL ) {
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
/* "single-cast" = cast over a NON-lazy parent. When the parent is
|
|
163
|
+
itself a lazy view family member, the cast contributes to a lazy
|
|
164
|
+
chain and should NOT be excluded from ca_is_lazy_view (otherwise
|
|
165
|
+
CArray.fuse chains break through the cast node, and `<=>` 1-pass fuse
|
|
166
|
+
degenerates back to 3-pass). Cast over entity / CAStride / CAFake /
|
|
167
|
+
etc. (= NOT a lazy parent) stays single-cast → eager binop dispatch
|
|
168
|
+
(so `1/zero` zero-check works). */
|
|
169
|
+
return ( p->obj_type != CA_OBJ_MONOP
|
|
170
|
+
&& p->obj_type != CA_OBJ_BINOP
|
|
171
|
+
&& p->obj_type != CA_OBJ_BINCMP
|
|
172
|
+
&& p->obj_type != CA_OBJ_MONCMP
|
|
173
|
+
&& p->obj_type != CA_OBJ_LAZY_MARKER );
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* ------------------------------------------------------------------- */
|
|
177
|
+
/* setup / new / free */
|
|
178
|
+
/* ------------------------------------------------------------------- */
|
|
179
|
+
|
|
180
|
+
static int
|
|
181
|
+
ca_monop_setup (CAMonOp *ca, CArray *parent, uint16_t op_id)
|
|
182
|
+
{
|
|
183
|
+
int8_t out_dt = ca_lazy_promote_monop(op_id, parent->data_type);
|
|
184
|
+
ca_size_t out_bytes = ca_sizeof[out_dt];
|
|
185
|
+
|
|
186
|
+
ca->obj_type = CA_OBJ_MONOP;
|
|
187
|
+
ca->data_type = out_dt;
|
|
188
|
+
/* A cast op_id encodes a value-converting cast (analogue of CAFake),
|
|
189
|
+
which is writable — `rb_ca_wrap_writable` returns the cast view
|
|
190
|
+
directly and write-back reverse-casts through ca_ptr2ptr. Non-cast
|
|
191
|
+
ops (= monop / monfunc) remain read-only. This matches the CAFake /
|
|
192
|
+
wrap_writable semantics. */
|
|
193
|
+
ca->flags = ca_monop_is_writable_view(op_id) ? 0 : CA_FLAG_READ_ONLY;
|
|
194
|
+
ca->ndim = parent->ndim;
|
|
195
|
+
ca->bytes = out_bytes;
|
|
196
|
+
ca->elements = parent->elements;
|
|
197
|
+
ca->ptr = NULL;
|
|
198
|
+
ca->mask = NULL;
|
|
199
|
+
if ( ! ca->_pool ) {
|
|
200
|
+
ca->dim = ALLOC_N(ca_size_t, parent->ndim);
|
|
201
|
+
}
|
|
202
|
+
ca->parent = parent;
|
|
203
|
+
ca->attach = 0;
|
|
204
|
+
ca->nosync = 0;
|
|
205
|
+
ca->op_id = op_id;
|
|
206
|
+
ca->data_class = Qnil; /* set by the builder when rtype is a class */
|
|
207
|
+
|
|
208
|
+
memcpy(ca->dim, parent->dim, parent->ndim * sizeof(ca_size_t));
|
|
209
|
+
|
|
210
|
+
if ( ca_has_mask(parent) ) {
|
|
211
|
+
ca_create_mask(ca);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if ( ca_is_scalar(parent) ) {
|
|
215
|
+
ca_set_flag(ca, CA_FLAG_SCALAR);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
CAMonOp *
|
|
222
|
+
ca_monop_new (CArray *parent, uint16_t op_id)
|
|
223
|
+
{
|
|
224
|
+
CAMonOp *ca = (CAMonOp *) ca_array_alloc(CA_OBJ_MONOP, parent->ndim);
|
|
225
|
+
ca_monop_setup(ca, parent, op_id);
|
|
226
|
+
return ca;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
static void
|
|
230
|
+
free_ca_monop (void *ap)
|
|
231
|
+
{
|
|
232
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
233
|
+
if ( ca != NULL ) {
|
|
234
|
+
ca_free(ca->mask);
|
|
235
|
+
if ( ca->_pool ) {
|
|
236
|
+
ca_array_free(ca); /* dim lives in _pool */
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
xfree(ca->dim);
|
|
240
|
+
xfree(ca);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* ------------------------------------------------------------------- */
|
|
246
|
+
/* operation function table */
|
|
247
|
+
/* ------------------------------------------------------------------- */
|
|
248
|
+
|
|
249
|
+
/* Forward declarations (cross-references between slot functions). */
|
|
250
|
+
static void ca_monop_func_xfer_stride (void *ap, ca_size_t *starts,
|
|
251
|
+
ca_size_t *counts, ca_size_t *strides,
|
|
252
|
+
void *data, int dir);
|
|
253
|
+
|
|
254
|
+
static void *
|
|
255
|
+
ca_monop_func_clone (void *ap)
|
|
256
|
+
{
|
|
257
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
258
|
+
return ca_monop_new(ca->parent, ca->op_id);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* per-cell access: walk the chain materialising 1 cell via per-cell
|
|
262
|
+
xfer_index that triggers materialise through the parent chain. This is
|
|
263
|
+
not catastrophic at typical depths, but hot loops should use a `.to_ca`
|
|
264
|
+
snapshot.
|
|
265
|
+
|
|
266
|
+
PUT path: when this view is a writable cast view (= cast op_id),
|
|
267
|
+
reverse-cast the incoming `data` (view data_type) to a per-cell scratch
|
|
268
|
+
in parent data_type, then ca_store_index into parent. Non-cast monop
|
|
269
|
+
views remain read-only (rb_raise). */
|
|
270
|
+
static void
|
|
271
|
+
ca_monop_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
272
|
+
{
|
|
273
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
274
|
+
ca_size_t starts[CA_RANK_MAX];
|
|
275
|
+
ca_size_t counts[CA_RANK_MAX];
|
|
276
|
+
ca_size_t strides[CA_RANK_MAX];
|
|
277
|
+
int8_t k;
|
|
278
|
+
ca_size_t s;
|
|
279
|
+
|
|
280
|
+
if ( dir != CA_XFER_GET ) {
|
|
281
|
+
/* PUT: only writable-view op_ids accept writes (cast, or byte_swap
|
|
282
|
+
involution). */
|
|
283
|
+
if ( ! ca_monop_is_writable_view(ca->op_id) ) {
|
|
284
|
+
rb_raise(rb_eRuntimeError, "CAMonOp is read-only (xfer_index PUT)");
|
|
285
|
+
}
|
|
286
|
+
{
|
|
287
|
+
char scratch[32]; /* max sizeof for any built-in numeric data_type */
|
|
288
|
+
ca_size_t bytes_per_cell = (ca_monop_is_cast(ca->op_id))
|
|
289
|
+
? ca->parent->bytes : ca->bytes;
|
|
290
|
+
char *p = (bytes_per_cell <= 32) ? scratch
|
|
291
|
+
: (char *) ALLOC_N(char, bytes_per_cell);
|
|
292
|
+
if ( ca_monop_is_cast(ca->op_id) ) {
|
|
293
|
+
/* Reverse-cast view-data_type `data` to parent data_type scratch, then
|
|
294
|
+
store into parent at idx[]. */
|
|
295
|
+
ca_ptr2ptr((CArray *) ca, data, ca->parent, p);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
/* byte_swap (involution): copy + byte_swap into parent-bytes
|
|
299
|
+
layout (= same bytes since data_type-preserving). */
|
|
300
|
+
memcpy(p, data, ca->bytes);
|
|
301
|
+
ca_byte_swap_buffer(ca->data_type, ca->bytes, 1, p);
|
|
302
|
+
}
|
|
303
|
+
ca_store_index(ca->parent, idx, p);
|
|
304
|
+
if ( p != scratch ) xfree(p);
|
|
305
|
+
}
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/* Single-cell region: counts[k] = 1 for all axes, starts = idx[]. */
|
|
310
|
+
s = ca->bytes;
|
|
311
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
312
|
+
starts[k] = idx[k];
|
|
313
|
+
counts[k] = 1;
|
|
314
|
+
strides[k] = s;
|
|
315
|
+
}
|
|
316
|
+
ca_monop_func_xfer_stride(ca, starts, counts, strides, data, CA_XFER_GET);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
static void
|
|
320
|
+
ca_monop_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
|
|
321
|
+
void *data, int dir)
|
|
322
|
+
{
|
|
323
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
324
|
+
ca_size_t i;
|
|
325
|
+
char *out = (char *) data;
|
|
326
|
+
|
|
327
|
+
if ( dir != CA_XFER_GET ) {
|
|
328
|
+
/* PUT: only writable-view op_ids accept writes. */
|
|
329
|
+
if ( ! ca_monop_is_writable_view(ca->op_id) ) {
|
|
330
|
+
rb_raise(rb_eRuntimeError, "CAMonOp is read-only (xfer_addrs PUT)");
|
|
331
|
+
}
|
|
332
|
+
/* Per-cell loop: forward to xfer_index PUT (handles cast/byte_swap
|
|
333
|
+
branch internally). */
|
|
334
|
+
for ( i = 0; i < n; i++ ) {
|
|
335
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
336
|
+
ca_addr2index((CArray *)ca, addrs[i], idx);
|
|
337
|
+
ca_monop_func_xfer_index(ca, idx, out + i * ca->bytes, CA_XFER_PUT);
|
|
338
|
+
}
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/* Naive per-addr loop: convert addr → idx, then xfer_index. This path
|
|
343
|
+
is not optimised. */
|
|
344
|
+
for ( i = 0; i < n; i++ ) {
|
|
345
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
346
|
+
ca_addr2index((CArray *)ca, addrs[i], idx);
|
|
347
|
+
ca_monop_func_xfer_index(ca, idx, out + i * ca->bytes, CA_XFER_GET);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* ------------------------------------------------------------------- */
|
|
352
|
+
/* In-place chain eval (scratch = 0) */
|
|
353
|
+
/* ------------------------------------------------------------------- */
|
|
354
|
+
|
|
355
|
+
/* Test counter: incremented every time xfer_stride allocates a leaf-side
|
|
356
|
+
scratch buffer (for the cast-before pull). Stays 0 across any depth
|
|
357
|
+
chain when no cast is needed, and 1 (cast scratch) regardless of chain
|
|
358
|
+
depth when the leaf needs widening. Reset and read from Ruby for
|
|
359
|
+
tests. */
|
|
360
|
+
ca_size_t ca_monop_scratch_acquire_count = 0;
|
|
361
|
+
|
|
362
|
+
/* Test counter: incremented every time CAMonOp's xfer_stride entry is
|
|
363
|
+
called. Used to pin that `inspect` / `dump_tree` do NOT materialise
|
|
364
|
+
(counter unchanged) while `each` / `to_a` / `sort` / `to_ca` etc. DO
|
|
365
|
+
(counter increments). */
|
|
366
|
+
ca_size_t ca_monop_materialise_call_count = 0;
|
|
367
|
+
|
|
368
|
+
#ifndef CA_MAX_LAZY_DEPTH
|
|
369
|
+
#define CA_MAX_LAZY_DEPTH 256
|
|
370
|
+
#endif
|
|
371
|
+
|
|
372
|
+
/* Apply a single op in-place on `data` (src == dst, unit element stride).
|
|
373
|
+
Raises if the kernel is not implemented for the given input data_type.
|
|
374
|
+
|
|
375
|
+
Unmask fast path:
|
|
376
|
+
We always pass `mask == NULL` to the kernel, which selects the SIMD-
|
|
377
|
+
friendly "no mask" branch in the existing eager kernels (see e.g.
|
|
378
|
+
ca_monop_sqrt_float64_t in carray_math.c — `if (m) { ... } else
|
|
379
|
+
{ ...SIMD loop... }`). Mask cells contain garbage after evaluation,
|
|
380
|
+
but they are unobservable: the output mask is built separately via
|
|
381
|
+
the attach lifecycle (ca_monop_func_create_mask + parent.mask
|
|
382
|
+
CARefer), so reads of masked cells return UNDEF regardless of byte
|
|
383
|
+
contents. This is the design license stated in CLAUDE.md
|
|
384
|
+
§"design premises" ("mask is not a protection mechanism").
|
|
385
|
+
|
|
386
|
+
A "partial mask slow path" (= a cell-wise branch to skip masked-cell
|
|
387
|
+
compute) is a possible future micro-optimisation; it is not done
|
|
388
|
+
because masked-cell compute does not affect correctness and the SIMD
|
|
389
|
+
path is faster overall for typical mask densities. */
|
|
390
|
+
static void
|
|
391
|
+
apply_monop_in_place (uint16_t op_id, int8_t cur_dt,
|
|
392
|
+
ca_size_t slab_n, void *data)
|
|
393
|
+
{
|
|
394
|
+
ca_monop_func_t fn = ca_monop_kernel_lookup(op_id, cur_dt);
|
|
395
|
+
if ( fn == NULL ) {
|
|
396
|
+
rb_raise(rb_eRuntimeError,
|
|
397
|
+
"CAMonOp: kernel not implemented (op_id=%u data_type=%d)",
|
|
398
|
+
(unsigned) op_id, (int) cur_dt);
|
|
399
|
+
}
|
|
400
|
+
fn(slab_n, NULL, (char *) data, 1, (char *) data, 1);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/* Minimal CArray-shaped stub used as ca_cast_block's data_type-only carrier.
|
|
404
|
+
ca_cast_block only reads data_type to index into ca_cast_func_table. */
|
|
405
|
+
typedef struct {
|
|
406
|
+
int16_t obj_type;
|
|
407
|
+
int8_t data_type;
|
|
408
|
+
} ca_data_type_stub_t;
|
|
409
|
+
|
|
410
|
+
/* Pull leaf into the output buffer. Three paths:
|
|
411
|
+
|
|
412
|
+
1. No size change (leaf.bytes == out.bytes): pull directly into
|
|
413
|
+
`data` using the caller's `strides` (= output byte layout).
|
|
414
|
+
Zero scratch; cur_dt = leaf.data_type.
|
|
415
|
+
|
|
416
|
+
2. Size mismatch + cast at leaf (`has_cast_at_leaf == 1`): allocate
|
|
417
|
+
a small scratch sized in leaf bytes, pull leaf into scratch with
|
|
418
|
+
row-major leaf-byte strides, then ca_cast_block scratch → data
|
|
419
|
+
(in the cast target's data_type). Scratch count = 1. cur_dt =
|
|
420
|
+
cast target data_type.
|
|
421
|
+
|
|
422
|
+
3. Size mismatch + cast mid-chain (`has_cast_at_leaf == 0`):
|
|
423
|
+
allocate scratch sized in leaf bytes, pull leaf into scratch
|
|
424
|
+
with leaf-byte strides, then memcpy scratch → start of `data`
|
|
425
|
+
(compact leaf-byte layout at the head of the larger buffer).
|
|
426
|
+
A later mid-chain cast op will expand it into the output layout.
|
|
427
|
+
Scratch count = 1. cur_dt = leaf.data_type.
|
|
428
|
+
|
|
429
|
+
The shared property: when output.bytes != leaf.bytes, the caller's
|
|
430
|
+
`strides` (= output byte layout) cannot be passed directly to
|
|
431
|
+
ca_xfer_stride on the leaf because they don't match leaf's per-cell
|
|
432
|
+
size. Pulling into a leaf-sized compact scratch decouples leaf-
|
|
433
|
+
layout from output-layout. */
|
|
434
|
+
static int8_t
|
|
435
|
+
pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
|
|
436
|
+
int has_cast_at_leaf, int8_t out_bytes,
|
|
437
|
+
ca_size_t slab_n,
|
|
438
|
+
ca_size_t *starts, ca_size_t *counts,
|
|
439
|
+
ca_size_t *strides, void *data)
|
|
440
|
+
{
|
|
441
|
+
ca_size_t leaf_bytes = leaf->bytes;
|
|
442
|
+
|
|
443
|
+
/* Path 1: no size mismatch, no cast — direct pull. */
|
|
444
|
+
if ( leaf_bytes == out_bytes && ! has_cast_at_leaf ) {
|
|
445
|
+
ca_xfer_stride(leaf, starts, counts, strides, data, CA_XFER_GET);
|
|
446
|
+
return leaf->data_type;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/* Paths 2 & 3 both need a leaf-sized scratch + leaf-byte strides
|
|
450
|
+
for the leaf pull. */
|
|
451
|
+
ca_size_t parent_strides[CA_RANK_MAX];
|
|
452
|
+
ca_size_t total_bytes = slab_n * leaf_bytes;
|
|
453
|
+
void *scratch;
|
|
454
|
+
volatile VALUE holder;
|
|
455
|
+
int8_t k;
|
|
456
|
+
{
|
|
457
|
+
ca_size_t s = leaf_bytes;
|
|
458
|
+
for ( k = leaf->ndim - 1; k >= 0; k-- ) {
|
|
459
|
+
parent_strides[k] = s;
|
|
460
|
+
s *= counts[k];
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
(void) holder;
|
|
464
|
+
scratch = ca_lazy_arena_acquire(total_bytes);
|
|
465
|
+
ca_monop_scratch_acquire_count++;
|
|
466
|
+
ca_xfer_stride(leaf, starts, counts, parent_strides, scratch, CA_XFER_GET);
|
|
467
|
+
|
|
468
|
+
int8_t result_dt;
|
|
469
|
+
if ( has_cast_at_leaf ) {
|
|
470
|
+
/* Path 2: cast scratch → data with output layout in target data_type. */
|
|
471
|
+
int8_t target_dt = (int8_t)(innermost_op - CA_MONOP_CAST_BASE);
|
|
472
|
+
ca_data_type_stub_t src_stub = { 0, leaf->data_type };
|
|
473
|
+
ca_data_type_stub_t dst_stub = { 0, target_dt };
|
|
474
|
+
ca_cast_block(slab_n, &src_stub, scratch, &dst_stub, data);
|
|
475
|
+
result_dt = target_dt;
|
|
476
|
+
} else {
|
|
477
|
+
/* Path 3: memcpy scratch → start of data. Mid-chain cast will
|
|
478
|
+
expand to output layout later. */
|
|
479
|
+
memcpy(data, scratch, total_bytes);
|
|
480
|
+
result_dt = leaf->data_type;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
ca_lazy_arena_release(scratch);
|
|
484
|
+
return result_dt;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
static void
|
|
488
|
+
ca_monop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
489
|
+
ca_size_t *strides, void *data, int dir)
|
|
490
|
+
{
|
|
491
|
+
CAMonOp *mo = (CAMonOp *) ap;
|
|
492
|
+
uint16_t op_chain[CA_MAX_LAZY_DEPTH];
|
|
493
|
+
int chain_len = 0;
|
|
494
|
+
CArray *leaf;
|
|
495
|
+
ca_size_t slab_n;
|
|
496
|
+
int8_t k;
|
|
497
|
+
int8_t cur_dt;
|
|
498
|
+
int i;
|
|
499
|
+
int has_cast_at_leaf = 0;
|
|
500
|
+
int chain_apply_count;
|
|
501
|
+
|
|
502
|
+
/* Writable-view op_ids (cast / byte_swap) use CAFake-style xfer_stride
|
|
503
|
+
for both GET and PUT. This avoids the chain-materialise inline path
|
|
504
|
+
that fails for a narrowing cast (parent.bytes > ca.bytes → buffer
|
|
505
|
+
overflow in the pull_leaf path 3). Mirrors the CAFake xfer_stride
|
|
506
|
+
fallback.
|
|
507
|
+
- cast: pstrides = strides/ca.bytes * parent.bytes; ca_cast_block.
|
|
508
|
+
- byte_swap: same data_type → pstrides = strides; involution apply. */
|
|
509
|
+
if ( ca_monop_is_writable_view(mo->op_id) ) {
|
|
510
|
+
int is_cast = ca_monop_is_cast(mo->op_id);
|
|
511
|
+
ca_size_t pstrides[CA_RANK_MAX];
|
|
512
|
+
ca_size_t n = 1;
|
|
513
|
+
ca_size_t parent_bytes = is_cast ? mo->parent->bytes : mo->bytes;
|
|
514
|
+
int8_t kk;
|
|
515
|
+
char *v;
|
|
516
|
+
volatile VALUE holder;
|
|
517
|
+
for ( kk = 0; kk < mo->ndim; kk++ ) {
|
|
518
|
+
n *= counts[kk];
|
|
519
|
+
pstrides[kk] = is_cast
|
|
520
|
+
? (strides[kk] / mo->bytes * mo->parent->bytes)
|
|
521
|
+
: strides[kk];
|
|
522
|
+
}
|
|
523
|
+
v = ALLOCV_N(char, holder, n * parent_bytes);
|
|
524
|
+
if ( dir == CA_XFER_GET ) {
|
|
525
|
+
ca_xfer_stride(mo->parent, starts, counts, pstrides, v, CA_XFER_GET);
|
|
526
|
+
if ( is_cast ) {
|
|
527
|
+
ca_cast_block(n, mo->parent, v, (CArray *) mo, data);
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
/* byte_swap (involution): apply on scratch then memcpy → data. */
|
|
531
|
+
ca_byte_swap_buffer(mo->data_type, mo->bytes, n, v);
|
|
532
|
+
memcpy(data, v, n * mo->bytes);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
if ( is_cast ) {
|
|
537
|
+
ca_cast_block(n, (CArray *) mo, data, mo->parent, v);
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
/* byte_swap PUT: copy data into scratch + byte_swap (involution). */
|
|
541
|
+
memcpy(v, data, n * mo->bytes);
|
|
542
|
+
ca_byte_swap_buffer(mo->data_type, mo->bytes, n, v);
|
|
543
|
+
}
|
|
544
|
+
ca_xfer_stride(mo->parent, starts, counts, pstrides, v, CA_XFER_PUT);
|
|
545
|
+
}
|
|
546
|
+
ALLOCV_END(holder);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/* Writable-view op_id PUT/GET is handled above. Below: non-writable
|
|
551
|
+
monop/monfunc. PUT is read-only. */
|
|
552
|
+
if ( dir != CA_XFER_GET ) {
|
|
553
|
+
rb_raise(rb_eRuntimeError, "CAMonOp is read-only (xfer_stride PUT)");
|
|
554
|
+
}
|
|
555
|
+
ca_monop_materialise_call_count++;
|
|
556
|
+
|
|
557
|
+
/* === 1. iterative chain collect ===
|
|
558
|
+
Walk down through CAMonOp parents, recording op_ids bottom-up.
|
|
559
|
+
Skip transparent CALazyMarker (collapse-on-consume already happened
|
|
560
|
+
at construction, but defensive in case a marker is mid-chain). */
|
|
561
|
+
leaf = (CArray *) mo;
|
|
562
|
+
while ( leaf->obj_type == CA_OBJ_MONOP ) {
|
|
563
|
+
CAMonOp *node = (CAMonOp *) leaf;
|
|
564
|
+
if ( chain_len >= CA_MAX_LAZY_DEPTH ) {
|
|
565
|
+
rb_raise(rb_eRuntimeError,
|
|
566
|
+
"CAMonOp chain depth exceeds CA_MAX_LAZY_DEPTH (%d)",
|
|
567
|
+
CA_MAX_LAZY_DEPTH);
|
|
568
|
+
}
|
|
569
|
+
op_chain[chain_len++] = node->op_id;
|
|
570
|
+
leaf = node->parent;
|
|
571
|
+
}
|
|
572
|
+
/* Skip a CALazyMarker leaf: transparent pass-through to its parent. */
|
|
573
|
+
while ( leaf->obj_type == CA_OBJ_LAZY_MARKER ) {
|
|
574
|
+
leaf = ((CAView *) leaf)->parent;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/* Detect whether the innermost recorded op is a cast. Cast nodes are
|
|
578
|
+
always inserted adjacent to the leaf at construction (cast-before),
|
|
579
|
+
so they appear at op_chain[chain_len - 1]. */
|
|
580
|
+
chain_apply_count = chain_len;
|
|
581
|
+
if ( chain_len > 0 && ca_monop_is_cast(op_chain[chain_len - 1]) ) {
|
|
582
|
+
has_cast_at_leaf = 1;
|
|
583
|
+
chain_apply_count = chain_len - 1;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/* === 2. pull leaf into output buffer (with optional cast) === */
|
|
587
|
+
slab_n = 1;
|
|
588
|
+
for ( k = 0; k < mo->ndim; k++ ) {
|
|
589
|
+
slab_n *= counts[k];
|
|
590
|
+
}
|
|
591
|
+
cur_dt = pull_leaf_with_optional_cast(
|
|
592
|
+
leaf,
|
|
593
|
+
has_cast_at_leaf ? op_chain[chain_len - 1] : 0,
|
|
594
|
+
has_cast_at_leaf,
|
|
595
|
+
(int8_t) mo->bytes,
|
|
596
|
+
slab_n, starts, counts, strides, data
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
/* === 3. apply remaining chain in reverse (leaf side → outermost) ===
|
|
600
|
+
Cast nodes may appear mid-chain when preserve ops precede a
|
|
601
|
+
widening monfunc (e.g. `int16.lazy.neg.sinh` inserts cast_f64
|
|
602
|
+
between neg and sinh). Handle them via ca_cast_block + scratch
|
|
603
|
+
similar to pull_leaf_with_optional_cast. */
|
|
604
|
+
for ( i = chain_apply_count - 1; i >= 0; i-- ) {
|
|
605
|
+
uint16_t op_id = op_chain[i];
|
|
606
|
+
if ( ca_monop_is_cast(op_id) ) {
|
|
607
|
+
int8_t target_dt = (int8_t)(op_id - CA_MONOP_CAST_BASE);
|
|
608
|
+
ca_size_t src_bytes = ca_sizeof[cur_dt];
|
|
609
|
+
ca_size_t total_bytes = slab_n * src_bytes;
|
|
610
|
+
void *scratch;
|
|
611
|
+
ca_data_type_stub_t src_stub = { 0, cur_dt };
|
|
612
|
+
ca_data_type_stub_t dst_stub = { 0, target_dt };
|
|
613
|
+
|
|
614
|
+
/* Copy current data → scratch (in src data_type), then cast scratch
|
|
615
|
+
→ data (in target data_type). Required because in-place cast
|
|
616
|
+
would overlap source/dest reads when target_bytes > src_bytes
|
|
617
|
+
(forward-walk reads cells past their own write boundary). */
|
|
618
|
+
scratch = ca_lazy_arena_acquire(total_bytes);
|
|
619
|
+
ca_monop_scratch_acquire_count++;
|
|
620
|
+
memcpy(scratch, data, total_bytes);
|
|
621
|
+
ca_cast_block(slab_n, &src_stub, scratch, &dst_stub, data);
|
|
622
|
+
ca_lazy_arena_release(scratch);
|
|
623
|
+
cur_dt = target_dt;
|
|
624
|
+
} else {
|
|
625
|
+
apply_monop_in_place(op_id, cur_dt, slab_n, data);
|
|
626
|
+
cur_dt = ca_lazy_promote_monop(op_id, cur_dt);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
static void
|
|
632
|
+
ca_monop_func_xfer_all (void *ap, void *data, int dir)
|
|
633
|
+
{
|
|
634
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
635
|
+
ca_size_t starts[CA_RANK_MAX];
|
|
636
|
+
ca_size_t native[CA_RANK_MAX];
|
|
637
|
+
int8_t k;
|
|
638
|
+
ca_size_t s = ca->bytes;
|
|
639
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
|
|
640
|
+
for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
|
|
641
|
+
ca_xfer_stride(ca, starts, ca->dim, native, data, dir);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
static void
|
|
645
|
+
ca_monop_func_allocate (void *ap)
|
|
646
|
+
{
|
|
647
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
648
|
+
/* A writable cast view's allocate path also needs the parent attached
|
|
649
|
+
so a later sync (sync slot → ca_cast_block(ca.ptr → parent.ptr))
|
|
650
|
+
finds parent.ptr valid. Caller pattern is `ca_allocate(view); fill
|
|
651
|
+
view.ptr; ca_sync(view); ca_detach(view)` (a write-only buffer
|
|
652
|
+
allocation that bypasses the parent data copy).
|
|
653
|
+
|
|
654
|
+
CAREFUL but safe: ca_attach / ca_allocate slots fire only on the
|
|
655
|
+
count 0→1 transition, so if both allocate and attach run on the same
|
|
656
|
+
view only the first triggers the slot; the second just increments the
|
|
657
|
+
count. Total ca_attach(parent) calls per view lifecycle = 1. CAFake
|
|
658
|
+
uses this same pattern. */
|
|
659
|
+
if ( ca_monop_is_writable_view(ca->op_id) ) {
|
|
660
|
+
ca_attach(ca->parent);
|
|
661
|
+
}
|
|
662
|
+
ca->ptr = xmalloc(ca_length(ca));
|
|
663
|
+
/* CA_OBJECT initialisation for GC safety. */
|
|
664
|
+
if ( ca->data_type == CA_OBJECT ) {
|
|
665
|
+
VALUE *p = (VALUE *) ca->ptr;
|
|
666
|
+
VALUE zero = SIZE2NUM(0);
|
|
667
|
+
ca_size_t i;
|
|
668
|
+
for ( i = 0; i < ca->elements; i++ ) {
|
|
669
|
+
*p++ = zero;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
static void
|
|
675
|
+
ca_monop_func_attach (void *ap)
|
|
676
|
+
{
|
|
677
|
+
/* CAREFUL: call the view-specific xfer_stride directly, bypassing the
|
|
678
|
+
public ca_xfer_stride dispatcher. A self-fill via the dispatcher
|
|
679
|
+
would hit the self-memcpy fast path (data == ca->ptr → no-op) and
|
|
680
|
+
leave the buffer as garbage. */
|
|
681
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
682
|
+
ca_size_t starts[CA_RANK_MAX];
|
|
683
|
+
ca_size_t native[CA_RANK_MAX];
|
|
684
|
+
int8_t k;
|
|
685
|
+
ca_size_t s;
|
|
686
|
+
|
|
687
|
+
/* A writable cast view uses the CAFake-style attach lifecycle
|
|
688
|
+
(= ca_attach(parent); ca_cast_block(parent.ptr → ca.ptr)). This
|
|
689
|
+
avoids the chain-materialise inline path, which assumes the output
|
|
690
|
+
buffer is at least as big as the leaf data and so fails for a
|
|
691
|
+
narrowing cast like float64→int32.
|
|
692
|
+
|
|
693
|
+
Non-cast (= chain materialise / read-only) uses the in-place chain
|
|
694
|
+
eval path. Its output buffer is in monop data_type; widening at the
|
|
695
|
+
leaf is supported via path 3 (mid-chain cast scratch expansion).
|
|
696
|
+
|
|
697
|
+
Mirrors CAFake.attach. */
|
|
698
|
+
if ( ca_monop_is_writable_view(ca->op_id) ) {
|
|
699
|
+
ca_attach(ca->parent);
|
|
700
|
+
ca->ptr = xmalloc(ca_length(ca));
|
|
701
|
+
/* CA_OBJECT initialisation for GC safety (mirrors CAFake.allocate):
|
|
702
|
+
zero-init the VALUE cells before any potential GC can observe
|
|
703
|
+
them. */
|
|
704
|
+
if ( ca->data_type == CA_OBJECT ) {
|
|
705
|
+
VALUE *p = (VALUE *) ca->ptr;
|
|
706
|
+
VALUE zero = SIZE2NUM(0);
|
|
707
|
+
ca_size_t i;
|
|
708
|
+
for ( i = 0; i < ca->elements; i++ ) {
|
|
709
|
+
*p++ = zero;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if ( ca_monop_is_cast(ca->op_id) ) {
|
|
713
|
+
/* Cast parent.ptr (parent data_type) → ca.ptr (cast target data_type). */
|
|
714
|
+
ca_cast_block(ca->elements, ca->parent, ca->parent->ptr,
|
|
715
|
+
(CArray *) ca, ca->ptr);
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
/* byte_swap: memcpy parent.ptr → ca.ptr (same data_type same bytes),
|
|
719
|
+
then byte_swap in place. */
|
|
720
|
+
memcpy(ca->ptr, ca->parent->ptr, ca_length(ca));
|
|
721
|
+
ca_byte_swap_buffer(ca->data_type, ca->bytes, ca->elements, ca->ptr);
|
|
722
|
+
}
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
ca->ptr = xmalloc(ca_length(ca));
|
|
727
|
+
|
|
728
|
+
s = ca->bytes;
|
|
729
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
|
|
730
|
+
for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
|
|
731
|
+
ca_monop_func_xfer_stride(ca, starts, ca->dim, native, ca->ptr, CA_XFER_GET);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
static void
|
|
735
|
+
ca_monop_func_sync (void *ap)
|
|
736
|
+
{
|
|
737
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
738
|
+
/* Writable cast lifecycle. A CABlock-on-CAMonOp(cast) sub-view fill
|
|
739
|
+
propagates via this path:
|
|
740
|
+
1. CABlock.attach pulls CAMonOp via xfer_stride GET into CABlock.ptr
|
|
741
|
+
2. user fills CABlock.ptr region
|
|
742
|
+
3. CABlock.sync writes CABlock.ptr back via xfer_stride PUT to CAMonOp
|
|
743
|
+
(fills the CAMonOp.ptr region in cast data_type)
|
|
744
|
+
4. CAMonOp.sync (= this) writes CAMonOp.ptr back to parent via a
|
|
745
|
+
reverse-cast bulk (ca_cast_block) + parent.sync
|
|
746
|
+
Non-cast (= chain materialise / read-only) skips this: no writes ever
|
|
747
|
+
accumulate in ca->ptr, so sync would be redundant. Mirrors the
|
|
748
|
+
CAFake sync pattern. */
|
|
749
|
+
if ( ! ca_monop_is_writable_view(ca->op_id) ) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if ( ca_monop_is_cast(ca->op_id) ) {
|
|
753
|
+
/* Cast view → parent: reverse-cast bulk. */
|
|
754
|
+
ca_update_mask(ca);
|
|
755
|
+
if ( ca->mask ) {
|
|
756
|
+
ca_cast_block_with_mask(ca->elements, ca, ca->ptr,
|
|
757
|
+
ca->parent, ca->parent->ptr,
|
|
758
|
+
(boolean8_t *) ca->parent->mask->ptr);
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
ca_cast_block(ca->elements, ca, ca->ptr, ca->parent, ca->parent->ptr);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
/* byte_swap (involution): apply on ca.ptr in place (re-swap), then
|
|
766
|
+
memcpy ca.ptr → parent.ptr. Mirrors CAByteSwap sync. */
|
|
767
|
+
ca_byte_swap_buffer(ca->data_type, ca->bytes, ca->elements, ca->ptr);
|
|
768
|
+
memcpy(ca->parent->ptr, ca->ptr, ca_length(ca));
|
|
769
|
+
}
|
|
770
|
+
ca_sync(ca->parent);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
static void
|
|
774
|
+
ca_monop_func_detach (void *ap)
|
|
775
|
+
{
|
|
776
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
777
|
+
xfree(ca->ptr);
|
|
778
|
+
ca->ptr = NULL;
|
|
779
|
+
/* A cast op_id attaches the parent in ca_monop_func_attach; pair it
|
|
780
|
+
here with one ca_detach(parent), so the attach/detach on the parent
|
|
781
|
+
balance across the view lifecycle. Mirrors CAFake detach. */
|
|
782
|
+
if ( ca_monop_is_writable_view(ca->op_id) ) {
|
|
783
|
+
ca_detach(ca->parent);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
static void
|
|
788
|
+
ca_monop_func_fill_data (void *ap, void *ptr)
|
|
789
|
+
{
|
|
790
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
791
|
+
/* Writable-view op_id (cast / byte_swap) fill path — operate on the
|
|
792
|
+
incoming scalar in view data_type, write the parent in parent
|
|
793
|
+
data_type (= same bytes for byte_swap, reverse-cast for cast).
|
|
794
|
+
Non-writable monop/monfunc remains read-only. */
|
|
795
|
+
if ( ! ca_monop_is_writable_view(ca->op_id) ) {
|
|
796
|
+
rb_raise(rb_eRuntimeError, "CAMonOp is read-only (fill_data)");
|
|
797
|
+
}
|
|
798
|
+
{
|
|
799
|
+
int is_cast = ca_monop_is_cast(ca->op_id);
|
|
800
|
+
ca_size_t parent_bytes = is_cast ? ca->parent->bytes : ca->bytes;
|
|
801
|
+
char stack_v[32];
|
|
802
|
+
char *v = (parent_bytes <= 32) ? stack_v
|
|
803
|
+
: (char *) xmalloc(parent_bytes);
|
|
804
|
+
if ( is_cast ) {
|
|
805
|
+
ca_ptr2ptr((CArray *) ca, ptr, ca->parent, v);
|
|
806
|
+
}
|
|
807
|
+
else {
|
|
808
|
+
memcpy(v, ptr, ca->bytes);
|
|
809
|
+
ca_byte_swap_buffer(ca->data_type, ca->bytes, 1, v);
|
|
810
|
+
}
|
|
811
|
+
ca_fill(ca->parent, v);
|
|
812
|
+
if ( v != stack_v ) xfree(v);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
static void
|
|
817
|
+
ca_monop_func_create_mask (void *ap)
|
|
818
|
+
{
|
|
819
|
+
CAMonOp *ca = (CAMonOp *) ap;
|
|
820
|
+
if ( ! ca->parent->mask ) {
|
|
821
|
+
ca_create_mask(ca->parent);
|
|
822
|
+
}
|
|
823
|
+
ca->mask =
|
|
824
|
+
(CArray *) ca_refer_new(ca->parent->mask,
|
|
825
|
+
CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
ca_operation_function_t ca_monop_func = {
|
|
829
|
+
-1, /* CA_OBJ_MONOP, set at install time */
|
|
830
|
+
CA_VIEW_ARRAY,
|
|
831
|
+
free_ca_monop,
|
|
832
|
+
ca_monop_func_clone,
|
|
833
|
+
ca_monop_func_allocate,
|
|
834
|
+
ca_monop_func_attach,
|
|
835
|
+
ca_monop_func_sync,
|
|
836
|
+
ca_monop_func_detach,
|
|
837
|
+
ca_monop_func_fill_data,
|
|
838
|
+
ca_monop_func_create_mask,
|
|
839
|
+
ca_monop_func_xfer_index,
|
|
840
|
+
ca_monop_func_xfer_addrs,
|
|
841
|
+
NULL, /* fold_stride: never-fold (op boundary) */
|
|
842
|
+
ca_monop_func_xfer_stride,
|
|
843
|
+
ca_monop_func_xfer_all,
|
|
844
|
+
};
|
|
845
|
+
|
|
846
|
+
/* ------------------------------------------------------------------- */
|
|
847
|
+
/* Public predicate + constructor */
|
|
848
|
+
/* ------------------------------------------------------------------- */
|
|
849
|
+
|
|
850
|
+
/* CABinOp obj_type. Declared in ca_obj_binop.c. */
|
|
851
|
+
extern int8_t CA_OBJ_BINOP;
|
|
852
|
+
/* CATriOp obj_type. Declared in ca_obj_triop.c. */
|
|
853
|
+
extern int8_t CA_OBJ_TRIOP;
|
|
854
|
+
/* CABinCmp / CAMonCmp. Declared in ca_obj_bincmp.c / ca_obj_moncmp.c. */
|
|
855
|
+
extern int8_t CA_OBJ_BINCMP;
|
|
856
|
+
extern int8_t CA_OBJ_MONCMP;
|
|
857
|
+
|
|
858
|
+
int
|
|
859
|
+
ca_is_lazy_view (void *ap)
|
|
860
|
+
{
|
|
861
|
+
CArray *ca = (CArray *) ap;
|
|
862
|
+
/* A single-cast CAMonOp (= structural successor of the CAFake numeric
|
|
863
|
+
view, see ca_monop_view_is_single_cast) is a value-converting
|
|
864
|
+
type-adapt view, NOT a lazy chain. Excluding it from is_lazy_view
|
|
865
|
+
preserves eager binop semantics (e.g. `a.as_int32 / b` calls the
|
|
866
|
+
eager div kernel with zero-check, not a CABinOp lazy build), matching
|
|
867
|
+
CAFake behaviour. Chain CAMonOp (depth >= 2) and non-cast
|
|
868
|
+
monop/monfunc remain lazy. */
|
|
869
|
+
if ( ca->obj_type == CA_OBJ_MONOP ) {
|
|
870
|
+
if ( ca_monop_view_is_single_cast(ca) ) {
|
|
871
|
+
return 0;
|
|
872
|
+
}
|
|
873
|
+
return 1;
|
|
874
|
+
}
|
|
875
|
+
return ( ca->obj_type == CA_OBJ_LAZY_MARKER ||
|
|
876
|
+
ca->obj_type == CA_OBJ_BINOP ||
|
|
877
|
+
ca->obj_type == CA_OBJ_TRIOP ||
|
|
878
|
+
ca->obj_type == CA_OBJ_BINCMP ||
|
|
879
|
+
ca->obj_type == CA_OBJ_MONCMP );
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/* Low-level CAMonOp constructor: wraps `cary` directly, no cast insertion.
|
|
883
|
+
Used internally by the public builder below for the cast node and the
|
|
884
|
+
final op node. */
|
|
885
|
+
static VALUE
|
|
886
|
+
rb_ca_monop_new (VALUE cary, uint16_t op_id)
|
|
887
|
+
{
|
|
888
|
+
volatile VALUE obj;
|
|
889
|
+
CArray *parent;
|
|
890
|
+
CAMonOp *ca;
|
|
891
|
+
rb_check_carray_object(cary);
|
|
892
|
+
TypedData_Get_Struct(cary, CArray, &carray_data_type, parent);
|
|
893
|
+
ca = ca_monop_new(parent, op_id);
|
|
894
|
+
obj = ca_wrap_struct(ca);
|
|
895
|
+
rb_ca_set_parent(obj, cary);
|
|
896
|
+
return obj;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/* Public builder: build a CAMonOp tree node for `op_id` over `cary`,
|
|
900
|
+
inserting a cast node if the existing eager kernel for op_id needs
|
|
901
|
+
a different input data_type than cary provides (cast-before).
|
|
902
|
+
|
|
903
|
+
If `cary` is a CALazyMarker, collapse-on-consume: use marker's parent
|
|
904
|
+
as the actual node parent so the marker doesn't appear in the tree. */
|
|
905
|
+
VALUE
|
|
906
|
+
rb_ca_monop_build (VALUE cary, uint16_t op_id)
|
|
907
|
+
{
|
|
908
|
+
CArray *parent;
|
|
909
|
+
VALUE target = cary;
|
|
910
|
+
int8_t parent_dt;
|
|
911
|
+
int8_t kernel_in_dt;
|
|
912
|
+
|
|
913
|
+
rb_check_carray_object(cary);
|
|
914
|
+
TypedData_Get_Struct(cary, CArray, &carray_data_type, parent);
|
|
915
|
+
|
|
916
|
+
/* Collapse-on-consume: if cary is a marker, replace it with its Ruby
|
|
917
|
+
parent for both the CAMonOp parent and the data_type probe. */
|
|
918
|
+
if ( parent->obj_type == CA_OBJ_LAZY_MARKER ) {
|
|
919
|
+
target = rb_ca_parent(cary);
|
|
920
|
+
TypedData_Get_Struct(target, CArray, &carray_data_type, parent);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
parent_dt = parent->data_type;
|
|
924
|
+
kernel_in_dt = ca_monop_kernel_input_data_type(op_id, parent_dt);
|
|
925
|
+
|
|
926
|
+
/* Insert cast node if the kernel wants a different input data_type. */
|
|
927
|
+
if ( kernel_in_dt != parent_dt ) {
|
|
928
|
+
uint16_t cast_op_id = CA_MONOP_CAST_BASE + (uint16_t) kernel_in_dt;
|
|
929
|
+
target = rb_ca_monop_new(target, cast_op_id);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
return rb_ca_monop_new(target, op_id);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/* Ruby surface: returns the configured CAMonOp. lib/carray/lazy.rb
|
|
936
|
+
invokes this from the per-op method redefinitions (`.lazy.<op>`). */
|
|
937
|
+
static VALUE
|
|
938
|
+
rb_ca_monop_s_build (VALUE klass, VALUE cary, VALUE op_id_val)
|
|
939
|
+
{
|
|
940
|
+
uint16_t op_id = (uint16_t) NUM2UINT(op_id_val);
|
|
941
|
+
(void) klass;
|
|
942
|
+
return rb_ca_monop_build(cary, op_id);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/* Ruby predicate: true if self is a lazy view (CAMonOp or CALazyMarker). */
|
|
946
|
+
static VALUE
|
|
947
|
+
rb_ca_is_lazy_view_p (VALUE self)
|
|
948
|
+
{
|
|
949
|
+
CArray *ca;
|
|
950
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
951
|
+
return ca_is_lazy_view(ca) ? Qtrue : Qfalse;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/* Read CAMonOp's op_id for inspect/dump_tree consumers in Ruby. */
|
|
955
|
+
static VALUE
|
|
956
|
+
rb_ca_monop_op_id (VALUE self)
|
|
957
|
+
{
|
|
958
|
+
CAMonOp *mo;
|
|
959
|
+
TypedData_Get_Struct(self, CAMonOp, &camonop_data_type, mo);
|
|
960
|
+
return UINT2NUM(mo->op_id);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/* ------------------------------------------------------------------- */
|
|
964
|
+
/* Ruby surface helpers */
|
|
965
|
+
/* ------------------------------------------------------------------- */
|
|
966
|
+
|
|
967
|
+
/* Reset and read the scratch-acquire counter used by tests. It stays 0
|
|
968
|
+
because in-place chain eval allocates no scratch; only attach (xfer_all
|
|
969
|
+
→ output) and the parent's own xfer_stride alloc, neither of which we
|
|
970
|
+
count here. */
|
|
971
|
+
static VALUE
|
|
972
|
+
rb_ca_monop_s_reset_scratch_counter (VALUE klass)
|
|
973
|
+
{
|
|
974
|
+
ca_monop_scratch_acquire_count = 0;
|
|
975
|
+
return Qnil;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
static VALUE
|
|
979
|
+
rb_ca_monop_s_scratch_count (VALUE klass)
|
|
980
|
+
{
|
|
981
|
+
return SIZE2NUM(ca_monop_scratch_acquire_count);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
static VALUE
|
|
985
|
+
rb_ca_monop_s_reset_materialise_counter (VALUE klass)
|
|
986
|
+
{
|
|
987
|
+
ca_monop_materialise_call_count = 0;
|
|
988
|
+
return Qnil;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
static VALUE
|
|
992
|
+
rb_ca_monop_s_materialise_count (VALUE klass)
|
|
993
|
+
{
|
|
994
|
+
return SIZE2NUM(ca_monop_materialise_call_count);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
static VALUE
|
|
998
|
+
rb_ca_monop_s_allocate (VALUE klass)
|
|
999
|
+
{
|
|
1000
|
+
CAMonOp *ca;
|
|
1001
|
+
return TypedData_Make_Struct(klass, CAMonOp, &camonop_data_type, ca);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
static VALUE
|
|
1005
|
+
rb_ca_monop_initialize_copy (VALUE self, VALUE other)
|
|
1006
|
+
{
|
|
1007
|
+
CAMonOp *ca, *cs;
|
|
1008
|
+
TypedData_Get_Struct(self, CAMonOp, &camonop_data_type, ca);
|
|
1009
|
+
TypedData_Get_Struct(other, CAMonOp, &camonop_data_type, cs);
|
|
1010
|
+
if ( ca_func[CA_OBJ_MONOP].pool_init ) {
|
|
1011
|
+
ca_array_pool_alloc(ca, CA_OBJ_MONOP, cs->parent->ndim);
|
|
1012
|
+
}
|
|
1013
|
+
ca_monop_setup(ca, cs->parent, cs->op_id);
|
|
1014
|
+
return self;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
void
|
|
1018
|
+
Init_ca_obj_monop (void)
|
|
1019
|
+
{
|
|
1020
|
+
rb_cCAMonOp = rb_define_class("CAMonOp", rb_cCAView);
|
|
1021
|
+
|
|
1022
|
+
ca_monop_func.struct_size = sizeof(CAMonOp);
|
|
1023
|
+
ca_monop_func.pool_bytes = ca_monop_pool_bytes;
|
|
1024
|
+
ca_monop_func.pool_init = ca_monop_pool_init;
|
|
1025
|
+
|
|
1026
|
+
CA_OBJ_MONOP = ca_install_obj_type(rb_cCAMonOp,
|
|
1027
|
+
&camonop_data_type,
|
|
1028
|
+
rb_cCArrayMask,
|
|
1029
|
+
&carray_mask_data_type, &ca_monop_func, sizeof(ca_monop_func));
|
|
1030
|
+
rb_define_const(rb_cObject, "CA_OBJ_MONOP", INT2NUM(CA_OBJ_MONOP));
|
|
1031
|
+
|
|
1032
|
+
/* op_id constants (all 34 ops). Cast op_ids are computed at
|
|
1033
|
+
call sites as CA_MONOP_CAST_BASE + data_type (no per-data_type constant). */
|
|
1034
|
+
rb_define_const(rb_cCAMonOp, "OP_ZERO", INT2NUM(CA_MONOP_ZERO));
|
|
1035
|
+
rb_define_const(rb_cCAMonOp, "OP_ONE", INT2NUM(CA_MONOP_ONE));
|
|
1036
|
+
rb_define_const(rb_cCAMonOp, "OP_FRAC", INT2NUM(CA_MONOP_FRAC));
|
|
1037
|
+
rb_define_const(rb_cCAMonOp, "OP_NEG", INT2NUM(CA_MONOP_NEG));
|
|
1038
|
+
rb_define_const(rb_cCAMonOp, "OP_BIT_NEG", INT2NUM(CA_MONOP_BIT_NEG));
|
|
1039
|
+
rb_define_const(rb_cCAMonOp, "OP_ABS_I", INT2NUM(CA_MONOP_ABS_I));
|
|
1040
|
+
rb_define_const(rb_cCAMonOp, "OP_CONJ", INT2NUM(CA_MONOP_CONJ));
|
|
1041
|
+
rb_define_const(rb_cCAMonOp, "OP_NOT", INT2NUM(CA_MONOP_NOT));
|
|
1042
|
+
rb_define_const(rb_cCAMonOp, "OP_CEIL", INT2NUM(CA_MONOP_CEIL));
|
|
1043
|
+
rb_define_const(rb_cCAMonOp, "OP_FLOOR", INT2NUM(CA_MONOP_FLOOR));
|
|
1044
|
+
rb_define_const(rb_cCAMonOp, "OP_ROUND", INT2NUM(CA_MONOP_ROUND));
|
|
1045
|
+
rb_define_const(rb_cCAMonOp, "OP_RCP", INT2NUM(CA_MONOP_RCP));
|
|
1046
|
+
rb_define_const(rb_cCAMonOp, "OP_RAD", INT2NUM(CA_MONOP_RAD));
|
|
1047
|
+
rb_define_const(rb_cCAMonOp, "OP_DEG", INT2NUM(CA_MONOP_DEG));
|
|
1048
|
+
rb_define_const(rb_cCAMonOp, "OP_SQRT", INT2NUM(CA_MONOP_SQRT));
|
|
1049
|
+
rb_define_const(rb_cCAMonOp, "OP_EXP", INT2NUM(CA_MONOP_EXP));
|
|
1050
|
+
rb_define_const(rb_cCAMonOp, "OP_EXP2", INT2NUM(CA_MONOP_EXP2));
|
|
1051
|
+
rb_define_const(rb_cCAMonOp, "OP_EXP10", INT2NUM(CA_MONOP_EXP10));
|
|
1052
|
+
rb_define_const(rb_cCAMonOp, "OP_LOG", INT2NUM(CA_MONOP_LOG));
|
|
1053
|
+
rb_define_const(rb_cCAMonOp, "OP_LOG10", INT2NUM(CA_MONOP_LOG10));
|
|
1054
|
+
rb_define_const(rb_cCAMonOp, "OP_LOG2", INT2NUM(CA_MONOP_LOG2));
|
|
1055
|
+
rb_define_const(rb_cCAMonOp, "OP_LOGB", INT2NUM(CA_MONOP_LOGB));
|
|
1056
|
+
rb_define_const(rb_cCAMonOp, "OP_SIN", INT2NUM(CA_MONOP_SIN));
|
|
1057
|
+
rb_define_const(rb_cCAMonOp, "OP_COS", INT2NUM(CA_MONOP_COS));
|
|
1058
|
+
rb_define_const(rb_cCAMonOp, "OP_TAN", INT2NUM(CA_MONOP_TAN));
|
|
1059
|
+
rb_define_const(rb_cCAMonOp, "OP_ASIN", INT2NUM(CA_MONOP_ASIN));
|
|
1060
|
+
rb_define_const(rb_cCAMonOp, "OP_ACOS", INT2NUM(CA_MONOP_ACOS));
|
|
1061
|
+
rb_define_const(rb_cCAMonOp, "OP_ATAN", INT2NUM(CA_MONOP_ATAN));
|
|
1062
|
+
rb_define_const(rb_cCAMonOp, "OP_SINH", INT2NUM(CA_MONOP_SINH));
|
|
1063
|
+
rb_define_const(rb_cCAMonOp, "OP_COSH", INT2NUM(CA_MONOP_COSH));
|
|
1064
|
+
rb_define_const(rb_cCAMonOp, "OP_TANH", INT2NUM(CA_MONOP_TANH));
|
|
1065
|
+
rb_define_const(rb_cCAMonOp, "OP_ASINH", INT2NUM(CA_MONOP_ASINH));
|
|
1066
|
+
rb_define_const(rb_cCAMonOp, "OP_ACOSH", INT2NUM(CA_MONOP_ACOSH));
|
|
1067
|
+
rb_define_const(rb_cCAMonOp, "OP_ATANH", INT2NUM(CA_MONOP_ATANH));
|
|
1068
|
+
rb_define_const(rb_cCAMonOp, "OP_IMAG_I", INT2NUM(CA_MONOP_IMAG_I));
|
|
1069
|
+
/* M.1: PyTorch alignment additions */
|
|
1070
|
+
rb_define_const(rb_cCAMonOp, "OP_EXPM1", INT2NUM(CA_MONOP_EXPM1));
|
|
1071
|
+
rb_define_const(rb_cCAMonOp, "OP_LOG1P", INT2NUM(CA_MONOP_LOG1P));
|
|
1072
|
+
rb_define_const(rb_cCAMonOp, "OP_RSQRT", INT2NUM(CA_MONOP_RSQRT));
|
|
1073
|
+
rb_define_const(rb_cCAMonOp, "OP_TRUNC", INT2NUM(CA_MONOP_TRUNC));
|
|
1074
|
+
rb_define_const(rb_cCAMonOp, "OP_SQUARE", INT2NUM(CA_MONOP_SQUARE));
|
|
1075
|
+
/* M.4: angle normalisation migration */
|
|
1076
|
+
rb_define_const(rb_cCAMonOp, "OP_DEG_360", INT2NUM(CA_MONOP_DEG_360));
|
|
1077
|
+
rb_define_const(rb_cCAMonOp, "OP_DEG_180", INT2NUM(CA_MONOP_DEG_180));
|
|
1078
|
+
rb_define_const(rb_cCAMonOp, "OP_RAD_2PI", INT2NUM(CA_MONOP_RAD_2PI));
|
|
1079
|
+
rb_define_const(rb_cCAMonOp, "OP_RAD_PI", INT2NUM(CA_MONOP_RAD_PI));
|
|
1080
|
+
rb_define_const(rb_cCAMonOp, "OP_SIGN", INT2NUM(CA_MONOP_SIGN));
|
|
1081
|
+
rb_define_const(rb_cCAMonOp, "OP_ARG_I", INT2NUM(CA_MONOP_ARG_I));
|
|
1082
|
+
rb_define_const(rb_cCAMonOp, "CAST_BASE", INT2NUM(CA_MONOP_CAST_BASE));
|
|
1083
|
+
|
|
1084
|
+
rb_define_alloc_func(rb_cCAMonOp, rb_ca_monop_s_allocate);
|
|
1085
|
+
rb_define_method(rb_cCAMonOp, "initialize_copy",
|
|
1086
|
+
rb_ca_monop_initialize_copy, 1);
|
|
1087
|
+
|
|
1088
|
+
/* Public builder (used by lib/carray/lazy.rb per-op method
|
|
1089
|
+
redefinitions). Inserts a cast node if needed (cast-before). */
|
|
1090
|
+
rb_define_singleton_method(rb_cCAMonOp, "__build__",
|
|
1091
|
+
rb_ca_monop_s_build, 2);
|
|
1092
|
+
|
|
1093
|
+
/* Lazy-view predicate exposed on CArray (covers CAMonOp and
|
|
1094
|
+
CALazyMarker). */
|
|
1095
|
+
rb_define_method(rb_cCArray, "__lazy_view__?",
|
|
1096
|
+
rb_ca_is_lazy_view_p, 0);
|
|
1097
|
+
|
|
1098
|
+
/* op_id accessor for inspect / dump_tree consumers in Ruby. */
|
|
1099
|
+
rb_define_method(rb_cCAMonOp, "__op_id__",
|
|
1100
|
+
rb_ca_monop_op_id, 0);
|
|
1101
|
+
|
|
1102
|
+
/* Test instrumentation (not user-facing API). */
|
|
1103
|
+
rb_define_singleton_method(rb_cCAMonOp, "__reset_scratch_counter__",
|
|
1104
|
+
rb_ca_monop_s_reset_scratch_counter, 0);
|
|
1105
|
+
rb_define_singleton_method(rb_cCAMonOp, "__scratch_count__",
|
|
1106
|
+
rb_ca_monop_s_scratch_count, 0);
|
|
1107
|
+
rb_define_singleton_method(rb_cCAMonOp, "__reset_materialise_counter__",
|
|
1108
|
+
rb_ca_monop_s_reset_materialise_counter, 0);
|
|
1109
|
+
rb_define_singleton_method(rb_cCAMonOp, "__materialise_count__",
|
|
1110
|
+
rb_ca_monop_s_materialise_count, 0);
|
|
1111
|
+
}
|