carray 2.0.1 → 3.0.1
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 +6 -25
- data/CHANGELOG.md +338 -0
- data/{NEWS.md → CHANGELOG.v1.md} +3 -0
- data/LICENSE +1 -1
- data/README.md +120 -36
- data/carray.gemspec +32 -30
- 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 +1244 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +124 -0
- data/ext/ca_binop_dispatch.h +152 -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 +239 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4367 -0
- data/ext/ca_kernel_iterator.h +2596 -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 +839 -0
- data/ext/ca_obj_binop.c +948 -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 +967 -0
- data/ext/ca_obj_face.c +750 -0
- data/ext/ca_obj_face.h +279 -0
- data/ext/ca_obj_fake.c +239 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +308 -0
- data/ext/ca_obj_grid.c +866 -440
- data/ext/ca_obj_meld.c +1039 -0
- data/ext/ca_obj_moncmp.c +588 -0
- data/ext/ca_obj_monop.c +1123 -0
- data/ext/ca_obj_object.c +866 -296
- data/ext/ca_obj_record.c +470 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +593 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +624 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1306 -0
- data/ext/ca_obj_shift.c +231 -793
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2584 -0
- data/ext/ca_obj_string.c +270 -0
- data/ext/ca_obj_tile.c +622 -0
- data/ext/ca_obj_time.c +548 -0
- data/ext/ca_obj_timedelta.c +437 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +785 -0
- data/ext/ca_obj_window.c +1202 -565
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +316 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +473 -0
- data/ext/ca_sweep_engine.h +166 -0
- data/ext/ca_transform_common.c +235 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +810 -420
- data/ext/carray_access.c +873 -731
- data/ext/carray_attribute.c +98 -329
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +376 -0
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +2897 -874
- data/ext/carray_call_cfunc.h +313 -0
- data/ext/carray_cast.c +1264 -315
- 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 +168 -270
- data/ext/carray_core.c +1396 -206
- 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 +1021 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +136 -0
- data/ext/carray_kernels_bincmp.c +4446 -0
- data/ext/carray_kernels_binop.c +11001 -0
- data/ext/carray_kernels_init.c +1131 -0
- data/ext/carray_kernels_map.c +3467 -0
- data/ext/carray_kernels_moncmp.c +2097 -0
- data/ext/carray_kernels_monop.c +18313 -0
- data/ext/carray_kernels_reduce_aggregate.c +25837 -0
- data/ext/carray_kernels_reduce_boolean.c +330 -0
- data/ext/carray_kernels_reduce_cumulative.c +14593 -0
- data/ext/carray_kernels_reduce_extreme.c +16948 -0
- data/ext/carray_kernels_reduce_variance.c +3910 -0
- data/ext/carray_kernels_scan.c +3693 -0
- data/ext/carray_kernels_search.c +32138 -0
- data/ext/carray_kernels_sort.c +10626 -0
- data/ext/carray_kernels_triop.c +1392 -0
- data/ext/carray_lazy.c +737 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +853 -158
- 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 +1650 -0
- data/ext/carray_operator.c +1525 -320
- 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 +234 -55
- data/ext/mk_call_cfunc.rb +671 -0
- data/ext/mkkernel.rb +9096 -0
- data/ext/ruby_carray.c +211 -108
- 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 +239 -0
- data/lib/carray/autoload_method_extension.rb +45 -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 +614 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1084 -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 +160 -328
- data/lib/carray/core_extensions.rb +297 -0
- data/lib/carray/data_type_extension.rb +250 -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 +642 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +321 -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 +316 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/fuse_source.rb +123 -0
- data/lib/carray/fusion.rb +218 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -26
- data/lib/carray/iterator.rb +58 -349
- data/lib/carray/lazy.rb +941 -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 +52 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +90 -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 +161 -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 +89 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +305 -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 +2654 -38
- data/lib/carray/window_iterator.rb +927 -0
- data/lib/carray.rb +55 -57
- data/yard-stubs/ca_obj_array.rb +385 -0
- data/yard-stubs/ca_obj_bitarray.rb +38 -0
- data/yard-stubs/ca_obj_bitfield.rb +43 -0
- data/yard-stubs/ca_obj_block.rb +73 -0
- data/yard-stubs/ca_obj_byte_swap.rb +56 -0
- data/yard-stubs/ca_obj_fake.rb +31 -0
- data/yard-stubs/ca_obj_farray.rb +32 -0
- data/yard-stubs/ca_obj_field.rb +45 -0
- data/yard-stubs/ca_obj_grid.rb +35 -0
- data/yard-stubs/ca_obj_refer.rb +72 -0
- data/yard-stubs/ca_obj_roll.rb +45 -0
- data/yard-stubs/ca_obj_shift.rb +43 -0
- data/yard-stubs/ca_obj_stride.rb +181 -0
- data/yard-stubs/ca_obj_tile.rb +29 -0
- data/yard-stubs/ca_obj_transpose.rb +40 -0
- data/yard-stubs/ca_obj_window.rb +49 -0
- data/yard-stubs/carray_access.rb +131 -0
- data/yard-stubs/carray_attribute.rb +246 -0
- data/yard-stubs/carray_broadcast.rb +37 -0
- data/yard-stubs/carray_cast.rb +489 -0
- data/yard-stubs/carray_class.rb +65 -0
- data/yard-stubs/carray_conversion.rb +76 -0
- data/yard-stubs/carray_copy.rb +79 -0
- data/yard-stubs/carray_core.rb +114 -0
- data/yard-stubs/carray_count.rb +79 -0
- data/yard-stubs/carray_element.rb +108 -0
- data/yard-stubs/carray_generate.rb +66 -0
- data/yard-stubs/carray_lazy.rb +23 -0
- data/yard-stubs/carray_loop.rb +140 -0
- data/yard-stubs/carray_mask.rb +259 -0
- data/yard-stubs/carray_math.rb +132 -0
- data/yard-stubs/carray_mathfunc.rb +45 -0
- data/yard-stubs/carray_median_percentile.rb +89 -0
- data/yard-stubs/carray_memory_view.rb +163 -0
- data/yard-stubs/carray_order.rb +312 -0
- data/yard-stubs/carray_random.rb +89 -0
- data/yard-stubs/carray_scatter.rb +106 -0
- data/yard-stubs/carray_slab.rb +57 -0
- data/yard-stubs/carray_sort.rb +163 -0
- data/yard-stubs/carray_test.rb +85 -0
- data/yard-stubs/carray_undef.rb +64 -0
- data/yard-stubs/carray_utils.rb +97 -0
- data/yard-stubs/ruby_carray.rb +193 -0
- metadata +220 -138
- 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/ca_obj_unbound_repeat.c +0 -529
- 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/mailmap +0 -1
- 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/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/lib/carray/lazy.rb
ADDED
|
@@ -0,0 +1,941 @@
|
|
|
1
|
+
# ---------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# Per-op lazy-or-eager method redefinition on CArray.
|
|
4
|
+
#
|
|
5
|
+
# For each of the monop/monfunc methods, alias the existing eager
|
|
6
|
+
# implementation as `__<name>_eager__` and redefine `<name>` to dispatch:
|
|
7
|
+
#
|
|
8
|
+
# - if self.__lazy_view__? → CAMonOp.__build__(self, OP_<NAME>)
|
|
9
|
+
# (cast node insertion handled in C if data_type mismatch)
|
|
10
|
+
# - else → __<name>_eager__
|
|
11
|
+
#
|
|
12
|
+
# CAREFUL: the redefinition must run once at load time AFTER the eager
|
|
13
|
+
# methods are bound by Init_carray_math. carray_ext.bundle Init order
|
|
14
|
+
# guarantees this file loads after ext-level binding.
|
|
15
|
+
#
|
|
16
|
+
# The lazy branch sits at the entry of CArray#<op> and does only a cheap
|
|
17
|
+
# type check, so the pure-eager path is essentially unaffected.
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
class CArray
|
|
21
|
+
# op_name => CAMonOp::OP_<NAME>
|
|
22
|
+
LAZY_MONOP_OP_IDS = {
|
|
23
|
+
# Preserve-data_type monop (8)
|
|
24
|
+
zero: CAMonOp::OP_ZERO,
|
|
25
|
+
one: CAMonOp::OP_ONE,
|
|
26
|
+
frac: CAMonOp::OP_FRAC,
|
|
27
|
+
neg: CAMonOp::OP_NEG,
|
|
28
|
+
bit_neg: CAMonOp::OP_BIT_NEG,
|
|
29
|
+
abs_i: CAMonOp::OP_ABS_I,
|
|
30
|
+
conj: CAMonOp::OP_CONJ,
|
|
31
|
+
not: CAMonOp::OP_NOT,
|
|
32
|
+
|
|
33
|
+
# Preserve-data_type monfunc (4)
|
|
34
|
+
ceil: CAMonOp::OP_CEIL,
|
|
35
|
+
floor: CAMonOp::OP_FLOOR,
|
|
36
|
+
round: CAMonOp::OP_ROUND,
|
|
37
|
+
rcp: CAMonOp::OP_RCP,
|
|
38
|
+
|
|
39
|
+
# Widening monfunc (22)
|
|
40
|
+
rad: CAMonOp::OP_RAD,
|
|
41
|
+
deg: CAMonOp::OP_DEG,
|
|
42
|
+
sqrt: CAMonOp::OP_SQRT,
|
|
43
|
+
exp: CAMonOp::OP_EXP,
|
|
44
|
+
exp2: CAMonOp::OP_EXP2,
|
|
45
|
+
exp10: CAMonOp::OP_EXP10,
|
|
46
|
+
log: CAMonOp::OP_LOG,
|
|
47
|
+
log10: CAMonOp::OP_LOG10,
|
|
48
|
+
log2: CAMonOp::OP_LOG2,
|
|
49
|
+
logb: CAMonOp::OP_LOGB,
|
|
50
|
+
sin: CAMonOp::OP_SIN,
|
|
51
|
+
cos: CAMonOp::OP_COS,
|
|
52
|
+
tan: CAMonOp::OP_TAN,
|
|
53
|
+
asin: CAMonOp::OP_ASIN,
|
|
54
|
+
acos: CAMonOp::OP_ACOS,
|
|
55
|
+
atan: CAMonOp::OP_ATAN,
|
|
56
|
+
sinh: CAMonOp::OP_SINH,
|
|
57
|
+
cosh: CAMonOp::OP_COSH,
|
|
58
|
+
tanh: CAMonOp::OP_TANH,
|
|
59
|
+
asinh: CAMonOp::OP_ASINH,
|
|
60
|
+
acosh: CAMonOp::OP_ACOSH,
|
|
61
|
+
atanh: CAMonOp::OP_ATANH,
|
|
62
|
+
|
|
63
|
+
# Additional monfunc
|
|
64
|
+
expm1: CAMonOp::OP_EXPM1,
|
|
65
|
+
log1p: CAMonOp::OP_LOG1P,
|
|
66
|
+
rsqrt: CAMonOp::OP_RSQRT,
|
|
67
|
+
trunc: CAMonOp::OP_TRUNC,
|
|
68
|
+
square: CAMonOp::OP_SQUARE,
|
|
69
|
+
|
|
70
|
+
# Angle normalisation
|
|
71
|
+
deg_360: CAMonOp::OP_DEG_360,
|
|
72
|
+
deg_180: CAMonOp::OP_DEG_180,
|
|
73
|
+
rad_2pi: CAMonOp::OP_RAD_2PI,
|
|
74
|
+
rad_pi: CAMonOp::OP_RAD_PI,
|
|
75
|
+
|
|
76
|
+
# Sign function (preserves the data type). bool/uint → 0/1, sint → -1/0/1,
|
|
77
|
+
# float → -1/0/1 NaN-preserving, complex → unit vector or 0.
|
|
78
|
+
sign: CAMonOp::OP_SIGN,
|
|
79
|
+
|
|
80
|
+
# imag_i: type-preserving primitive (0 for numeric, cimag for complex
|
|
81
|
+
# in the real slot). Primarily consumed by the `imag` special case
|
|
82
|
+
# below but also directly callable via `a.lazy.imag_i`; entry here
|
|
83
|
+
# so the direct call fuses instead of falling to eager.
|
|
84
|
+
imag_i: CAMonOp::OP_IMAG_I,
|
|
85
|
+
}.freeze
|
|
86
|
+
|
|
87
|
+
LAZY_MONOP_OP_IDS.each do |name, op_id|
|
|
88
|
+
eager_alias = :"__#{name}_eager__"
|
|
89
|
+
# Preserve the existing eager method under a hidden alias.
|
|
90
|
+
alias_method(eager_alias, name)
|
|
91
|
+
# Capture the eager UnboundMethod for fast direct dispatch (avoids
|
|
92
|
+
# Ruby method-lookup overhead of `__send__` on the hot eager path).
|
|
93
|
+
eager_um = instance_method(eager_alias)
|
|
94
|
+
# Redefine as lazy-or-eager dispatch.
|
|
95
|
+
define_method(name) do
|
|
96
|
+
if __lazy_view__?
|
|
97
|
+
CAMonOp.__build__(self, op_id)
|
|
98
|
+
else
|
|
99
|
+
eager_um.bind_call(self)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Operator aliases (`~` -> bit_neg, `-@` -> neg) were installed by
|
|
105
|
+
# rb_define_alias at Init time (see MkKernel.alias_monop in
|
|
106
|
+
# ext/carray_kernels_monop.c) BEFORE this file redefines the target
|
|
107
|
+
# methods. rb_define_alias snapshots the method entry, so the operator
|
|
108
|
+
# form still points at the original eager C function and skips the
|
|
109
|
+
# lazy dispatch above — `~a` / `-a` on a lazy view materialise instead
|
|
110
|
+
# of building a CAMonOp node. Re-alias here so the operator form
|
|
111
|
+
# follows the redefined lazy-or-eager dispatch.
|
|
112
|
+
alias_method :"~", :bit_neg
|
|
113
|
+
alias_method :"-@", :neg
|
|
114
|
+
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
# abs lazy fuse (post-IC follow-up): the hand-written rb_ca_abs in
|
|
117
|
+
# ext/carray_math.c is NOT in LAZY_MONOP_OP_IDS because it does
|
|
118
|
+
# complex-specific dispatch (= magnitude as float). Re-express that
|
|
119
|
+
# dispatch via chain composition over existing lazy ops so abs rides
|
|
120
|
+
# the lazy substrate fully:
|
|
121
|
+
# numeric parent: CAMonOp(OP_ABS_I) (1 node)
|
|
122
|
+
# complex parent: CAMonOp(cast_<float>) ∘ CAMonOp(OP_ABS_I) (2 nodes)
|
|
123
|
+
# The complex chain works because ca_monop_abs_i stores |z| in the
|
|
124
|
+
# real slot with imag=0, then the cast picks up the real part —
|
|
125
|
+
# semantically identical to the eager abs. The cast target is the
|
|
126
|
+
# real component width of the operand (cmplx64 -> f32, cmplx128 ->
|
|
127
|
+
# f64), matching the eager kernel's `complex: :real_of_source`.
|
|
128
|
+
# No new C kernel, no new op_id required.
|
|
129
|
+
# ---------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
# @private
|
|
132
|
+
alias_method :__abs_eager__, :abs
|
|
133
|
+
__abs_eager_um__ = instance_method(:__abs_eager__)
|
|
134
|
+
define_method(:abs) do
|
|
135
|
+
if __lazy_view__?
|
|
136
|
+
abs_i_node = CAMonOp.__build__(self, CAMonOp::OP_ABS_I)
|
|
137
|
+
if complex?
|
|
138
|
+
# float_dt is a Symbol; convert via data_type_code so the cast
|
|
139
|
+
# op_id can be computed by Integer arithmetic.
|
|
140
|
+
float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
|
|
141
|
+
CAMonOp.__build__(abs_i_node,
|
|
142
|
+
CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
|
|
143
|
+
else
|
|
144
|
+
abs_i_node
|
|
145
|
+
end
|
|
146
|
+
else
|
|
147
|
+
__abs_eager_um__.bind_call(self)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
# real / imag lazy fuse (post-IC follow-up):
|
|
153
|
+
#
|
|
154
|
+
# Eager `real`/`imag` in lib/carray/math.rb return CAField views
|
|
155
|
+
# (zero-copy, mutable byte-offset access into complex storage) or
|
|
156
|
+
# fresh template entities for non-complex. Both break a lazy chain
|
|
157
|
+
# when applied to a lazy parent (CAField materialises the parent into
|
|
158
|
+
# an entity to byte-offset into). For lazy parents we re-express via
|
|
159
|
+
# chain composition so the chain stays fused:
|
|
160
|
+
#
|
|
161
|
+
# complex parent .real → CAMonOp(cast_<float>)
|
|
162
|
+
# (cmplx128→f64 / cmplx64→f32 casts pick up the real part —
|
|
163
|
+
# verified equivalent to existing CAField path for read-only
|
|
164
|
+
# consumption)
|
|
165
|
+
#
|
|
166
|
+
# complex parent .imag → CAMonOp(cast_<float>) ∘ CAMonOp(imag_i)
|
|
167
|
+
# (imag_i puts cimag in the real component of a same-data_type slot,
|
|
168
|
+
# cast then extracts it as float — same trick as abs)
|
|
169
|
+
#
|
|
170
|
+
# non-complex parent .real → self (= pass-through, chain unchanged)
|
|
171
|
+
#
|
|
172
|
+
# non-complex parent .imag → CAMonOp(imag_i) (= same-shape lazy zero)
|
|
173
|
+
#
|
|
174
|
+
# Eager parents fall through to the existing math.rb implementation
|
|
175
|
+
# (= CAField for complex, CARefer/template for non-complex) so the
|
|
176
|
+
# mutable-setter use cases (`.real = val` / `.imag = val`) remain
|
|
177
|
+
# supported. Lazy views are inherently read-only so the loss of
|
|
178
|
+
# mutability on the lazy path is not a regression.
|
|
179
|
+
# ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
# @private
|
|
182
|
+
alias_method :__real_eager__, :real
|
|
183
|
+
__real_eager_um__ = instance_method(:__real_eager__)
|
|
184
|
+
define_method(:real) do
|
|
185
|
+
if __lazy_view__?
|
|
186
|
+
if complex?
|
|
187
|
+
float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
|
|
188
|
+
# float_dt is a Symbol; convert via data_type_code for kernel
|
|
189
|
+
# op_id arithmetic.
|
|
190
|
+
CAMonOp.__build__(self,
|
|
191
|
+
CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
|
|
192
|
+
else
|
|
193
|
+
# `real` of a real array is the array itself; preserve the lazy
|
|
194
|
+
# chain unchanged (eager returns self[] = CARefer wrap, but for
|
|
195
|
+
# lazy chain consumption returning self keeps streaming intact).
|
|
196
|
+
self
|
|
197
|
+
end
|
|
198
|
+
else
|
|
199
|
+
__real_eager_um__.bind_call(self)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# @!visibility private
|
|
204
|
+
alias_method :__imag_eager__, :imag
|
|
205
|
+
__imag_eager_um__ = instance_method(:__imag_eager__)
|
|
206
|
+
define_method(:imag) do
|
|
207
|
+
if __lazy_view__?
|
|
208
|
+
imag_i_node = CAMonOp.__build__(self, CAMonOp::OP_IMAG_I)
|
|
209
|
+
if complex?
|
|
210
|
+
float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
|
|
211
|
+
# float_dt is a Symbol; convert via data_type_code for kernel
|
|
212
|
+
# op_id arithmetic.
|
|
213
|
+
CAMonOp.__build__(imag_i_node,
|
|
214
|
+
CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
|
|
215
|
+
else
|
|
216
|
+
imag_i_node
|
|
217
|
+
end
|
|
218
|
+
else
|
|
219
|
+
__imag_eager_um__.bind_call(self)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# ---------------------------------------------------------------------------
|
|
224
|
+
# arg lazy fuse: eager `arg` is a data_type-changing monop (see
|
|
225
|
+
# MkKernel.monop :arg output rule).
|
|
226
|
+
# CAMonOp's cast-before invariant requires input data type == output data type
|
|
227
|
+
# at each in-place step, so `arg` can't sit directly in the substrate.
|
|
228
|
+
# Chain compose via the type-preserving `arg_i` primitive:
|
|
229
|
+
#
|
|
230
|
+
# integer / bool parent → cast_f64 ∘ arg_i (result f64)
|
|
231
|
+
# float parent → arg_i (result same float)
|
|
232
|
+
# complex parent → cast_<float> ∘ arg_i (arg_i writes real
|
|
233
|
+
# component of complex slot; cast extracts
|
|
234
|
+
# the real part, same trick as abs / imag)
|
|
235
|
+
#
|
|
236
|
+
# Both paths keep the operand's float / complex width (f32 → f32,
|
|
237
|
+
# cmplx64 → f32), and both send integers to f64 since pi does not fit
|
|
238
|
+
# an integer slot. That agrees with the general lazy-substrate rule
|
|
239
|
+
# "scalar keeps operand's precision" (see ca_lazy_wrap_scalar header).
|
|
240
|
+
# ---------------------------------------------------------------------------
|
|
241
|
+
|
|
242
|
+
# @private
|
|
243
|
+
alias_method :__arg_eager__, :arg
|
|
244
|
+
__arg_eager_um__ = instance_method(:__arg_eager__)
|
|
245
|
+
define_method(:arg) do
|
|
246
|
+
if __lazy_view__?
|
|
247
|
+
parent = self
|
|
248
|
+
if integer? || boolean?
|
|
249
|
+
parent = CAMonOp.__build__(parent,
|
|
250
|
+
CAMonOp::CAST_BASE +
|
|
251
|
+
CArray.data_type_code(CA_FLOAT64))
|
|
252
|
+
end
|
|
253
|
+
arg_i_node = CAMonOp.__build__(parent, CAMonOp::OP_ARG_I)
|
|
254
|
+
if complex?
|
|
255
|
+
float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
|
|
256
|
+
CAMonOp.__build__(arg_i_node,
|
|
257
|
+
CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
|
|
258
|
+
else
|
|
259
|
+
arg_i_node
|
|
260
|
+
end
|
|
261
|
+
else
|
|
262
|
+
__arg_eager_um__.bind_call(self)
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# ---------------------------------------------------------------------------
|
|
267
|
+
# Binop dispatch. Operator entries are redefined so a lazy operand on
|
|
268
|
+
# either side routes into CABinOp.__build__. Op scope:
|
|
269
|
+
# - 5 arithmetic: + - * / **
|
|
270
|
+
# - 3 bitwise: & | ^
|
|
271
|
+
# - 2 shifts: << >>
|
|
272
|
+
# - 2 misc: %, rcp_mul
|
|
273
|
+
# ---------------------------------------------------------------------------
|
|
274
|
+
|
|
275
|
+
LAZY_BINOP_OP_IDS = {
|
|
276
|
+
:+ => CABinOp::OP_ADD,
|
|
277
|
+
:- => CABinOp::OP_SUB,
|
|
278
|
+
:* => CABinOp::OP_MUL,
|
|
279
|
+
:/ => CABinOp::OP_DIV,
|
|
280
|
+
:** => CABinOp::OP_POW,
|
|
281
|
+
:& => CABinOp::OP_BIT_AND,
|
|
282
|
+
:| => CABinOp::OP_BIT_OR,
|
|
283
|
+
:^ => CABinOp::OP_BIT_XOR,
|
|
284
|
+
:<< => CABinOp::OP_BIT_LSHIFT,
|
|
285
|
+
:>> => CABinOp::OP_BIT_RSHIFT,
|
|
286
|
+
:% => CABinOp::OP_MOD,
|
|
287
|
+
:rcp_mul => CABinOp::OP_RCP_MUL,
|
|
288
|
+
|
|
289
|
+
# Float-only binops registered eagerly by mkkernel; the lazy entries
|
|
290
|
+
# here pick them up so `a.lazy.hypot(b)` etc. ride the substrate.
|
|
291
|
+
:copysign => CABinOp::OP_COPYSIGN,
|
|
292
|
+
:logaddexp => CABinOp::OP_LOGADDEXP,
|
|
293
|
+
:nextafter => CABinOp::OP_NEXTAFTER,
|
|
294
|
+
:fmod => CABinOp::OP_FMOD,
|
|
295
|
+
:atan2 => CABinOp::OP_ATAN2,
|
|
296
|
+
:hypot => CABinOp::OP_HYPOT,
|
|
297
|
+
|
|
298
|
+
# Pair-wise max / min (NaN-skip via C99 fmax/fmin on float branch).
|
|
299
|
+
:pmax => CABinOp::OP_PMAX,
|
|
300
|
+
:pmin => CABinOp::OP_PMIN,
|
|
301
|
+
|
|
302
|
+
# Pair-wise max / min, NaN-propagate variant.
|
|
303
|
+
:maximum => CABinOp::OP_MAXIMUM,
|
|
304
|
+
:minimum => CABinOp::OP_MINIMUM,
|
|
305
|
+
|
|
306
|
+
# Boolean word forms (bool + object; plain mask propagation, no
|
|
307
|
+
# Kleene fixup — see the CA_BINOP_AND note in ca_binop_dispatch.h).
|
|
308
|
+
:and => CABinOp::OP_AND,
|
|
309
|
+
:or => CABinOp::OP_OR,
|
|
310
|
+
:xor => CABinOp::OP_XOR,
|
|
311
|
+
|
|
312
|
+
# IEEE 754 remainder (distinct semantics from `%` / `mod`: float
|
|
313
|
+
# branch uses C99 `remainder`, round-half-to-even).
|
|
314
|
+
}.freeze
|
|
315
|
+
|
|
316
|
+
LAZY_BINOP_OP_IDS.each do |method_name, op_id|
|
|
317
|
+
eager_alias = :"__#{method_name}_eager__"
|
|
318
|
+
alias_method(eager_alias, method_name)
|
|
319
|
+
eager_um = instance_method(eager_alias)
|
|
320
|
+
define_method(method_name) do |other|
|
|
321
|
+
if __lazy_view__? || (other.is_a?(CArray) && other.__lazy_view__?)
|
|
322
|
+
CABinOp.__build__(self, other, op_id)
|
|
323
|
+
else
|
|
324
|
+
eager_um.bind_call(self, other)
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Binop word aliases (`add` -> `+`, `mul` -> `*`, `bit_and` -> `&`, …) were
|
|
330
|
+
# installed by rb_define_alias at Init time (see MkKernel.alias_binop in
|
|
331
|
+
# ext/carray_kernels_*.c) BEFORE the redefinitions above. The C alias
|
|
332
|
+
# snapshots the original eager method entry, so the word form still points
|
|
333
|
+
# at the original C function and skips lazy dispatch — `a.lazy.add(1)`
|
|
334
|
+
# materialises instead of building a CABinOp node. Re-alias here so the
|
|
335
|
+
# word form follows the redefined operator method.
|
|
336
|
+
{
|
|
337
|
+
add: :+,
|
|
338
|
+
sub: :-,
|
|
339
|
+
mul: :*,
|
|
340
|
+
div: :/,
|
|
341
|
+
mod: :%,
|
|
342
|
+
bit_and: :&,
|
|
343
|
+
bit_or: :|,
|
|
344
|
+
bit_xor: :^,
|
|
345
|
+
bit_lshift: :<<,
|
|
346
|
+
bit_rshift: :>>,
|
|
347
|
+
}.each do |word, op|
|
|
348
|
+
alias_method(word, op)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# `pow` is the canonical C method (defined by rb_define_method in
|
|
352
|
+
# ext/ca_op_ipower.c) that `**` is rb_define_alias'd to. The `**`
|
|
353
|
+
# override in this file (with the ipower Integer-exponent fast path)
|
|
354
|
+
# does not propagate to `pow` through the C alias, so `a.lazy.pow(2)`
|
|
355
|
+
# falls to eager. Re-alias so `pow` follows the redefined `**`.
|
|
356
|
+
alias_method :pow, :**
|
|
357
|
+
|
|
358
|
+
# `fmax` / `fmin` are rb_define_alias'd to `pmax` / `pmin` at Init
|
|
359
|
+
# time (same C-alias-snapshot trap). Re-alias so the C99-familiar
|
|
360
|
+
# spellings follow the redefined lazy dispatch above.
|
|
361
|
+
alias_method :fmax, :pmax
|
|
362
|
+
alias_method :fmin, :pmin
|
|
363
|
+
|
|
364
|
+
# ---------------------------------------------------------------------------
|
|
365
|
+
# Triop dispatch (fma / fms / clip). CATriOp is the CABinOp analog for
|
|
366
|
+
# three-operand element-wise ops. Each Ruby method redefined below
|
|
367
|
+
# dispatches to CATriOp.__build__ when any of self / op2 / op3 is a
|
|
368
|
+
# lazy view, and falls to the eager C method otherwise.
|
|
369
|
+
#
|
|
370
|
+
# `clip` is the strict-clamp entry (`__clip_ki__`, called by the
|
|
371
|
+
# lib/carray/basics.rb `clip` wrapper's both-bounds-present path). The
|
|
372
|
+
# nil-bound one-sided cases route through the wrapper's `pmax` / `pmin`
|
|
373
|
+
# calls, which themselves lazy-fuse via LAZY_BINOP_OP_IDS above — so
|
|
374
|
+
# `a.lazy.clip(nil, hi)` and `a.lazy.clip(lo, nil)` fuse without a
|
|
375
|
+
# dedicated triop entry.
|
|
376
|
+
# ---------------------------------------------------------------------------
|
|
377
|
+
|
|
378
|
+
LAZY_TRIOP_OP_IDS = {
|
|
379
|
+
fma: CATriOp::OP_FMA,
|
|
380
|
+
fms: CATriOp::OP_FMS,
|
|
381
|
+
__clip_ki__: CATriOp::OP_CLIP,
|
|
382
|
+
}.freeze
|
|
383
|
+
|
|
384
|
+
LAZY_TRIOP_OP_IDS.each do |method_name, op_id|
|
|
385
|
+
eager_alias = :"__#{method_name}_eager_triop__"
|
|
386
|
+
alias_method(eager_alias, method_name)
|
|
387
|
+
eager_um = instance_method(eager_alias)
|
|
388
|
+
define_method(method_name) do |op2, op3|
|
|
389
|
+
lazy_receiver = __lazy_view__? ||
|
|
390
|
+
(op2.is_a?(CArray) && op2.__lazy_view__?) ||
|
|
391
|
+
(op3.is_a?(CArray) && op3.__lazy_view__?)
|
|
392
|
+
if lazy_receiver
|
|
393
|
+
CATriOp.__build__(self, op2, op3, op_id)
|
|
394
|
+
else
|
|
395
|
+
eager_um.bind_call(self, op2, op3)
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# `**` integer-exponent lazy fast path.
|
|
401
|
+
#
|
|
402
|
+
# The default OP_POW path above goes through the `power` binop kernel,
|
|
403
|
+
# which uses `pow(x, p)` / `cpow(x, p)` (transcendental, ~20-50 ns/cell)
|
|
404
|
+
# for float / complex parents. When `other` is an Integer, the eager
|
|
405
|
+
# `rb_ca_pow` dispatches to `rb_ca_ipower` (= binary exponentiation via
|
|
406
|
+
# `op_powi_<type>`, ~1-3 mul/cell, 3-5x faster than the transcendental
|
|
407
|
+
# form). Mirror that dispatch here so `a.lazy ** 2` rides the same
|
|
408
|
+
# fast path.
|
|
409
|
+
#
|
|
410
|
+
# Routing rule (matches eager `rb_ca_pow`):
|
|
411
|
+
# - lazy parent + Integer exponent + Float/Complex parent data_type
|
|
412
|
+
# -> wrap exponent as CScalar.int64 + CABinOp(OP_IPOWER)
|
|
413
|
+
# - lazy parent (other cases) -> CABinOp(OP_POW) [existing]
|
|
414
|
+
# - else -> eager (which itself dispatches to ipower internally)
|
|
415
|
+
#
|
|
416
|
+
# Integer parents intentionally stay on OP_POW: the mkkernel-generated
|
|
417
|
+
# `power` binop kernel already uses `op_powi_<type>` for integer data_types
|
|
418
|
+
# via the `int:` expr in MkKernel.binop :power, so OP_POW + int parent
|
|
419
|
+
# is already fast. The lazy-substrate gap was Float/Complex only.
|
|
420
|
+
__pow_eager_um__ = instance_method(:"__**_eager__")
|
|
421
|
+
define_method(:**) do |other|
|
|
422
|
+
if other.is_a?(Integer) && (float? || complex?) &&
|
|
423
|
+
(__lazy_view__? || (other.is_a?(CArray) && other.__lazy_view__?))
|
|
424
|
+
CABinOp.__build__(self, CScalar.int64.tap { |s| s[0] = other },
|
|
425
|
+
CABinOp::OP_IPOWER)
|
|
426
|
+
elsif __lazy_view__? || (other.is_a?(CArray) && other.__lazy_view__?)
|
|
427
|
+
CABinOp.__build__(self, other, CABinOp::OP_POW)
|
|
428
|
+
else
|
|
429
|
+
__pow_eager_um__.bind_call(self, other)
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# coerce: when self is a lazy view and the scalar appears on the LEFT
|
|
434
|
+
# (e.g. `2 * a.lazy`), Ruby's Numeric#* calls a.lazy.coerce(2). The
|
|
435
|
+
# default coerce would unwrap the lazy-ness via eager scalar promotion;
|
|
436
|
+
# here we keep it lazy by returning [scalar_as_cscalar, self], so the
|
|
437
|
+
# subsequent operator call ends up with a lazy receiver and triggers
|
|
438
|
+
# the CABinOp builder.
|
|
439
|
+
# ---------------------------------------------------------------------------
|
|
440
|
+
# bincmp / moncmp dispatch.
|
|
441
|
+
#
|
|
442
|
+
# Comparison output is always boolean8_t, so it cannot reuse the CABinOp
|
|
443
|
+
# in-place trick. CABinCmp pulls both operands into operand-data_type
|
|
444
|
+
# scratches and writes boolean to the output buffer. Integer is_nan /
|
|
445
|
+
# is_inf / is_finite use existing per-data_type kernels (which handle the
|
|
446
|
+
# integer const-false/true result and mask skip).
|
|
447
|
+
#
|
|
448
|
+
# Scope: 7 bincmp + 3 moncmp + operator aliases (`<` / `>` / `<=` / `>=`)
|
|
449
|
+
# for canonical Ruby comparison syntax. Note: `==` / `eql?` are NOT
|
|
450
|
+
# comparison ops — eager `CArray#==` (rb_ca_equal) is array-level
|
|
451
|
+
# equality returning bool, not element-wise. Element-wise equality is
|
|
452
|
+
# `eq` / `feq`.
|
|
453
|
+
#
|
|
454
|
+
# `feq` is arity 1 in eager (compile-time FLT_EPSILON / DBL_EPSILON);
|
|
455
|
+
# we mirror that here. Runtime eps is a future extension (struct field
|
|
456
|
+
# already reserved).
|
|
457
|
+
# ---------------------------------------------------------------------------
|
|
458
|
+
|
|
459
|
+
LAZY_BINCMP_OP_IDS = {
|
|
460
|
+
# Canonical method names + operator aliases (rb_define_alias in C
|
|
461
|
+
# creates separate dispatch entries, so we override both).
|
|
462
|
+
:lt => CABinCmp::OP_LT,
|
|
463
|
+
:< => CABinCmp::OP_LT,
|
|
464
|
+
:gt => CABinCmp::OP_GT,
|
|
465
|
+
:> => CABinCmp::OP_GT,
|
|
466
|
+
:le => CABinCmp::OP_LE,
|
|
467
|
+
:<= => CABinCmp::OP_LE,
|
|
468
|
+
:ge => CABinCmp::OP_GE,
|
|
469
|
+
:>= => CABinCmp::OP_GE,
|
|
470
|
+
:eq => CABinCmp::OP_EQ,
|
|
471
|
+
:ne => CABinCmp::OP_NE,
|
|
472
|
+
:feq => CABinCmp::OP_FEQ,
|
|
473
|
+
}.freeze
|
|
474
|
+
|
|
475
|
+
LAZY_BINCMP_OP_IDS.each do |method_name, op_id|
|
|
476
|
+
eager_alias = :"__#{method_name}_eager_bincmp__"
|
|
477
|
+
alias_method(eager_alias, method_name)
|
|
478
|
+
eager_um = instance_method(eager_alias)
|
|
479
|
+
define_method(method_name) do |other|
|
|
480
|
+
if __lazy_view__? || (other.is_a?(CArray) && other.__lazy_view__?)
|
|
481
|
+
CABinCmp.__build__(self, other, op_id)
|
|
482
|
+
else
|
|
483
|
+
eager_um.bind_call(self, other)
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# tolerance-bearing bincmp ops (= is_close / is_equiv) use the same
|
|
489
|
+
# CABinCmp dispatch but with a 2nd `tol` positional arg. Kept in a
|
|
490
|
+
# separate dict because the LAZY_BINCMP_OP_IDS define_method block above
|
|
491
|
+
# uses arity 1 (|other|); these need arity 2 (|other, tol|).
|
|
492
|
+
LAZY_BINCMP_TOL_OP_IDS = {
|
|
493
|
+
:is_close => CABinCmp::OP_IS_CLOSE,
|
|
494
|
+
:is_equiv => CABinCmp::OP_IS_EQUIV,
|
|
495
|
+
}.freeze
|
|
496
|
+
|
|
497
|
+
LAZY_BINCMP_TOL_OP_IDS.each do |method_name, op_id|
|
|
498
|
+
eager_alias = :"__#{method_name}_eager_bincmp_tol__"
|
|
499
|
+
alias_method(eager_alias, method_name)
|
|
500
|
+
eager_um = instance_method(eager_alias)
|
|
501
|
+
define_method(method_name) do |other, tol|
|
|
502
|
+
if __lazy_view__? || (other.is_a?(CArray) && other.__lazy_view__?)
|
|
503
|
+
CABinCmp.__build__(self, other, op_id, tol.to_f.abs)
|
|
504
|
+
else
|
|
505
|
+
eager_um.bind_call(self, other, tol)
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# @!visibility private
|
|
511
|
+
LAZY_MONCMP_OP_IDS = {
|
|
512
|
+
is_nan: CAMonCmp::OP_IS_NAN,
|
|
513
|
+
is_inf: CAMonCmp::OP_IS_INF,
|
|
514
|
+
is_finite: CAMonCmp::OP_IS_FINITE,
|
|
515
|
+
is_invalid: CAMonCmp::OP_IS_INVALID,
|
|
516
|
+
signbit: CAMonCmp::OP_SIGNBIT,
|
|
517
|
+
}.freeze
|
|
518
|
+
|
|
519
|
+
LAZY_MONCMP_OP_IDS.each do |name, op_id|
|
|
520
|
+
eager_alias = :"__#{name}_eager_moncmp__"
|
|
521
|
+
alias_method(eager_alias, name)
|
|
522
|
+
eager_um = instance_method(eager_alias)
|
|
523
|
+
define_method(name) do
|
|
524
|
+
if __lazy_view__?
|
|
525
|
+
CAMonCmp.__build__(self, op_id)
|
|
526
|
+
else
|
|
527
|
+
eager_um.bind_call(self)
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# @!visibility private
|
|
533
|
+
alias_method :__coerce_eager__, :coerce
|
|
534
|
+
coerce_um = instance_method(:__coerce_eager__)
|
|
535
|
+
define_method(:coerce) do |other|
|
|
536
|
+
if __lazy_view__?
|
|
537
|
+
dt_name = data_type_name
|
|
538
|
+
if CScalar.respond_to?(dt_name)
|
|
539
|
+
s = CScalar.public_send(dt_name)
|
|
540
|
+
s[0] = other
|
|
541
|
+
[s, self]
|
|
542
|
+
else
|
|
543
|
+
coerce_um.bind_call(self, other)
|
|
544
|
+
end
|
|
545
|
+
else
|
|
546
|
+
coerce_um.bind_call(self, other)
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
# ---------------------------------------------------------------------------
|
|
552
|
+
# CAMonOp / CALazyMarker surface methods.
|
|
553
|
+
#
|
|
554
|
+
# Materialise-on-first-touch for Enumerable receivers, MV export reject,
|
|
555
|
+
# inspect/dump_tree summary that does NOT materialise.
|
|
556
|
+
# ---------------------------------------------------------------------------
|
|
557
|
+
|
|
558
|
+
class CAMonOp
|
|
559
|
+
# op_id => :name reverse lookup
|
|
560
|
+
OP_NAMES = CArray::LAZY_MONOP_OP_IDS.invert.freeze
|
|
561
|
+
|
|
562
|
+
# --- inspect / dump_tree (no materialise) ----------------------------
|
|
563
|
+
|
|
564
|
+
def op_label
|
|
565
|
+
op_id = __op_id__
|
|
566
|
+
if op_id >= CAMonOp::CAST_BASE
|
|
567
|
+
"cast_#{CArray.data_type_name(op_id - CAMonOp::CAST_BASE)}"
|
|
568
|
+
else
|
|
569
|
+
(OP_NAMES[op_id] || "op_#{op_id}").to_s
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
private :op_label
|
|
573
|
+
|
|
574
|
+
# @overload inspect
|
|
575
|
+
# Returns a short summary of the lazy chain without
|
|
576
|
+
# materialising.
|
|
577
|
+
# @return [String]
|
|
578
|
+
def inspect
|
|
579
|
+
"#<CAMonOp #{op_label}(#{parent.inspect})>"
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
# @overload to_s
|
|
583
|
+
# Alias of {#inspect}.
|
|
584
|
+
# @return [String]
|
|
585
|
+
def to_s
|
|
586
|
+
inspect
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
# @overload dump_tree(indent = 0)
|
|
590
|
+
# Returns an ASCII summary of the lazy expression tree.
|
|
591
|
+
# Does not materialise.
|
|
592
|
+
# @param indent [Integer] leading indentation depth.
|
|
593
|
+
# @return [String]
|
|
594
|
+
def dump_tree(indent = 0)
|
|
595
|
+
pad = " " * indent
|
|
596
|
+
out = +"#{pad}#{op_label}\n"
|
|
597
|
+
out << if parent.is_a?(CAMonOp)
|
|
598
|
+
parent.dump_tree(indent + 1)
|
|
599
|
+
else
|
|
600
|
+
"#{" " * (indent + 1)}#{parent.class}(#{parent.data_type_name}, #{parent.shape.inspect})\n"
|
|
601
|
+
end
|
|
602
|
+
out
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# @overload each { |value| ... }
|
|
606
|
+
# Materialises `self` and yields each element of the resulting
|
|
607
|
+
# entity.
|
|
608
|
+
# @yieldparam value [Object]
|
|
609
|
+
# @return [Object]
|
|
610
|
+
def each(&block)
|
|
611
|
+
to_ca.each(&block)
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
# @overload to_a
|
|
615
|
+
# Materialises `self` and returns the resulting Array.
|
|
616
|
+
# @return [Array]
|
|
617
|
+
def to_a
|
|
618
|
+
to_ca.to_a
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
# @overload sort(*args, **kwargs)
|
|
622
|
+
# Materialises `self` into a copy and returns
|
|
623
|
+
# `entity.sort(*args, **kwargs)`.
|
|
624
|
+
# @return [CArray]
|
|
625
|
+
def sort(*args, **kwargs)
|
|
626
|
+
copy.sort(*args, **kwargs)
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
# --- MemoryView export → TypeError + .copy recommendation -----------
|
|
630
|
+
|
|
631
|
+
# bulk-memory-view / Numo / Apache Arrow consumers go through the MV
|
|
632
|
+
# protocol, which requires a contiguous backing buffer. Lazy views
|
|
633
|
+
# have no backing buffer until materialised; raise with a clear hint.
|
|
634
|
+
#
|
|
635
|
+
# MemoryView is consulted via to_memory_view_internal on the receiver
|
|
636
|
+
# in some setups; the safest cross-cutting hook is the to_memory_view
|
|
637
|
+
# method (defined by carray_memory_view producer). We override to
|
|
638
|
+
# raise. Importers that probe via `rb_memory_view_get` will hit the
|
|
639
|
+
# producer slot, which we DON'T currently override at C-level — but
|
|
640
|
+
# the MV producer for CAView rejects backing-bufferless views by
|
|
641
|
+
# default; tests below verify both Ruby-level and C-level reject.
|
|
642
|
+
# @overload to_memory_view
|
|
643
|
+
# Not supported: a lazy view has no backing buffer.
|
|
644
|
+
# @return [void]
|
|
645
|
+
# @raise [TypeError] always; call `.copy` first.
|
|
646
|
+
def to_memory_view
|
|
647
|
+
raise TypeError,
|
|
648
|
+
"lazy view (CAMonOp) has no backing buffer; call `.copy` " \
|
|
649
|
+
"first to materialise into an entity before exporting via " \
|
|
650
|
+
"MemoryView."
|
|
651
|
+
end
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
# Lazy element-wise view of a binary operation on two operands. Nothing is
|
|
655
|
+
# computed until the chain is forced, so a composed expression evaluates in
|
|
656
|
+
# one pass instead of materialising an intermediate per operator.
|
|
657
|
+
#
|
|
658
|
+
# Produced by an operator on `.lazy` operands or inside `CArray.fuse`, not
|
|
659
|
+
# constructed directly.
|
|
660
|
+
class CABinOp
|
|
661
|
+
# op_id => :name reverse lookup
|
|
662
|
+
OP_NAMES = CArray::LAZY_BINOP_OP_IDS.invert.merge(
|
|
663
|
+
OP_IPOWER => :ipow,
|
|
664
|
+
).freeze
|
|
665
|
+
|
|
666
|
+
def op_label
|
|
667
|
+
op_id = __op_id__
|
|
668
|
+
(OP_NAMES[op_id] || "op_#{op_id}").to_s
|
|
669
|
+
end
|
|
670
|
+
private :op_label
|
|
671
|
+
|
|
672
|
+
# @overload inspect
|
|
673
|
+
# Returns a summary of the binop chain without materialising.
|
|
674
|
+
# @return [String]
|
|
675
|
+
def inspect
|
|
676
|
+
"#<CABinOp #{op_label}(#{parent.inspect}, #{__binop_right__.inspect})>"
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
# @overload to_s
|
|
680
|
+
# Alias of {#inspect}.
|
|
681
|
+
# @return [String]
|
|
682
|
+
def to_s
|
|
683
|
+
inspect
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
# @overload dump_tree(indent = 0)
|
|
687
|
+
# Returns an ASCII summary of the lazy expression tree, walking
|
|
688
|
+
# both children. Does not materialise.
|
|
689
|
+
# @return [String]
|
|
690
|
+
def dump_tree(indent = 0)
|
|
691
|
+
pad = " " * indent
|
|
692
|
+
out = +"#{pad}#{op_label}\n"
|
|
693
|
+
[parent, __binop_right__].each do |child|
|
|
694
|
+
out << if child.is_a?(CAMonOp) || child.is_a?(CABinOp)
|
|
695
|
+
child.dump_tree(indent + 1)
|
|
696
|
+
else
|
|
697
|
+
"#{" " * (indent + 1)}#{child.class}(#{child.data_type_name}, #{child.shape.inspect})\n"
|
|
698
|
+
end
|
|
699
|
+
end
|
|
700
|
+
out
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
# @overload each { |value| ... }
|
|
704
|
+
# Materialises `self` and yields each element.
|
|
705
|
+
# @return [Object]
|
|
706
|
+
def each(&block)
|
|
707
|
+
to_ca.each(&block)
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
# @overload to_a
|
|
711
|
+
# Materialises `self` and returns the resulting Array.
|
|
712
|
+
# @return [Array]
|
|
713
|
+
def to_a
|
|
714
|
+
to_ca.to_a
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
# @overload sort(*args, **kwargs)
|
|
718
|
+
# Materialises `self` and sorts the resulting entity.
|
|
719
|
+
# @return [CArray]
|
|
720
|
+
def sort(*args, **kwargs)
|
|
721
|
+
to_ca.sort(*args, **kwargs)
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
# @overload to_memory_view
|
|
725
|
+
# Not supported for lazy binops.
|
|
726
|
+
# @raise [TypeError] always.
|
|
727
|
+
def to_memory_view
|
|
728
|
+
raise TypeError,
|
|
729
|
+
"lazy view (CABinOp) has no backing buffer; call `.to_ca` " \
|
|
730
|
+
"first to materialise into an entity before exporting via " \
|
|
731
|
+
"MemoryView."
|
|
732
|
+
end
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
class CALazyMarker
|
|
736
|
+
# @overload inspect
|
|
737
|
+
# Returns a short summary showing the wrapped entity.
|
|
738
|
+
# @return [String]
|
|
739
|
+
def inspect
|
|
740
|
+
"#<CALazyMarker over #{parent.inspect}>"
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
# @overload to_s
|
|
744
|
+
# Alias of {#inspect}.
|
|
745
|
+
# @return [String]
|
|
746
|
+
def to_s
|
|
747
|
+
inspect
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
# @overload dump_tree(indent = 0)
|
|
751
|
+
# Returns an ASCII summary of the marker and its parent entity.
|
|
752
|
+
# @return [String]
|
|
753
|
+
def dump_tree(indent = 0)
|
|
754
|
+
pad = " " * indent
|
|
755
|
+
"#{pad}lazy(marker)\n" \
|
|
756
|
+
"#{" " * (indent + 1)}#{parent.class}(#{parent.data_type_name}, #{parent.shape.inspect})\n"
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
# @overload each { |value| ... }
|
|
760
|
+
# Yields each element of the wrapped entity (no materialise
|
|
761
|
+
# needed).
|
|
762
|
+
# @return [Object]
|
|
763
|
+
def each(&block)
|
|
764
|
+
parent.each(&block)
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
# @overload to_a
|
|
768
|
+
# Returns the wrapped entity's Array.
|
|
769
|
+
# @return [Array]
|
|
770
|
+
def to_a
|
|
771
|
+
parent.to_a
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
# @overload to_memory_view
|
|
775
|
+
# Not supported: markers do not own a backing buffer.
|
|
776
|
+
# @raise [TypeError] always; call `.copy` first.
|
|
777
|
+
def to_memory_view
|
|
778
|
+
raise TypeError,
|
|
779
|
+
"lazy marker (CALazyMarker) has no backing buffer; call " \
|
|
780
|
+
"`.copy` first to materialise into an entity before " \
|
|
781
|
+
"exporting via MemoryView."
|
|
782
|
+
end
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
# ---------------------------------------------------------------------------
|
|
786
|
+
# Lazy views override `to_ca` to force evaluation.
|
|
787
|
+
#
|
|
788
|
+
# `CArray#to_ca` returns `self` (a concrete-backed CArray — entity or data
|
|
789
|
+
# view — is already a CArray, no copy needed). A lazy view has no data until
|
|
790
|
+
# evaluated, so `to_ca` must produce it: it materialises into a new entity,
|
|
791
|
+
# matching the Ruby `Enumerable#to_a` / lazy `force` convention. (`copy` is
|
|
792
|
+
# the materialise path; on a lazy view it evaluates the expression.)
|
|
793
|
+
#
|
|
794
|
+
# That entity is detached from the operands, so writes to it reach nothing:
|
|
795
|
+
# `writable: true` is refused rather than answered with a result that would
|
|
796
|
+
# swallow them.
|
|
797
|
+
# ---------------------------------------------------------------------------
|
|
798
|
+
[CAMonOp, CABinOp, CATriOp, CAMonCmp, CABinCmp, CALazyMarker].each do |klass|
|
|
799
|
+
klass.class_eval do
|
|
800
|
+
def to_ca(writable: false)
|
|
801
|
+
if writable
|
|
802
|
+
raise "#{self.class}#to_ca evaluates into a new entity; " \
|
|
803
|
+
"it can't satisfy `writable: true'"
|
|
804
|
+
end
|
|
805
|
+
copy
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
# Where something is registered to compute an expression -- a compiler,
|
|
809
|
+
# say -- it is asked here, and the walk below is what happens when it
|
|
810
|
+
# declines, when nothing is registered, or when the array is small
|
|
811
|
+
# enough that walking is the faster answer.
|
|
812
|
+
def copy
|
|
813
|
+
return super unless CArray.expression_evaluator
|
|
814
|
+
CArray::Fusion.evaluate(self) || super
|
|
815
|
+
end
|
|
816
|
+
end
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
# ---------------------------------------------------------------------------
|
|
820
|
+
# CArray.fuse
|
|
821
|
+
#
|
|
822
|
+
# Transient lazy-fusion scope: wraps each CArray argument with `.lazy`,
|
|
823
|
+
# yields the wrappers (and any non-CArray args pass-through unchanged)
|
|
824
|
+
# to the block, then on block exit auto-materialises a bare lazy view
|
|
825
|
+
# return value into an entity. Non-lazy return values (Numeric, plain
|
|
826
|
+
# CArray, Array, nil, etc.) pass through as-is.
|
|
827
|
+
#
|
|
828
|
+
# Design notes:
|
|
829
|
+
# - Non-CArray args pass through eager (= polymorphic numeric helper
|
|
830
|
+
# idiom: `magnus(25.0)` → Float and `magnus(arr)` → CArray carried by
|
|
831
|
+
# a single helper definition, combined with the `CArray::CoreExtensions`
|
|
832
|
+
# refinement which gives Float/Integer/Rational the same postfix math
|
|
833
|
+
# API as CArray instances).
|
|
834
|
+
# - Shadow args are read-only (CA_FLAG_READ_ONLY via `.lazy`), so
|
|
835
|
+
# destructive ops on them raise naturally.
|
|
836
|
+
# - A nested fuse materialises at its own inner exit.
|
|
837
|
+
# - A bare lazy return auto-materialises; escaping the lazy view (e.g.
|
|
838
|
+
# stashing it in an Array) is the user's responsibility.
|
|
839
|
+
# ---------------------------------------------------------------------------
|
|
840
|
+
# CArray.fuse
|
|
841
|
+
#
|
|
842
|
+
# Builds an expression instead of evaluating it, so that it is computed in
|
|
843
|
+
# one pass with no array standing for a step along the way. The block is
|
|
844
|
+
# read rather than called; see CArray::FuseSource for why, and for what
|
|
845
|
+
# happens where its source cannot be read.
|
|
846
|
+
# ---------------------------------------------------------------------------
|
|
847
|
+
class << CArray
|
|
848
|
+
# The object asked to compute an expression, or nil. CArray can always
|
|
849
|
+
# walk one, so nothing has to be registered and nothing changes when
|
|
850
|
+
# nothing is; what a registered evaluator adds is a second way to arrive
|
|
851
|
+
# at the same answer, and it may decline.
|
|
852
|
+
#
|
|
853
|
+
# It is called as `call(plan, out)` -- see {CArray::Fusion} for what a
|
|
854
|
+
# plan holds -- and answers by filling `out` and returning something
|
|
855
|
+
# true, or by returning something false having written nothing. Raising
|
|
856
|
+
# is not an answer: one that raises is dropped, with a warning, and
|
|
857
|
+
# expressions are walked from there on.
|
|
858
|
+
#
|
|
859
|
+
# @return [#call, nil]
|
|
860
|
+
attr_reader :expression_evaluator
|
|
861
|
+
|
|
862
|
+
# @param object [#call, nil] see {#expression_evaluator}
|
|
863
|
+
# @raise [ArgumentError] when it cannot be called.
|
|
864
|
+
def expression_evaluator= (object)
|
|
865
|
+
if object && ! object.respond_to?(:call)
|
|
866
|
+
raise ArgumentError,
|
|
867
|
+
"an expression evaluator must respond to #call(plan, out)"
|
|
868
|
+
end
|
|
869
|
+
@expression_evaluator = object
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
# @overload fuse { <expression> }
|
|
873
|
+
# Builds the expression rather than evaluating it, so that it is
|
|
874
|
+
# computed in one pass with no array standing for a step along the way.
|
|
875
|
+
#
|
|
876
|
+
# The block is not called. Its source is read, every name in it that
|
|
877
|
+
# holds a CArray is given `.lazy`, and the result is evaluated back in
|
|
878
|
+
# the block's own binding -- so `self`, instance variables, methods and
|
|
879
|
+
# constants are what they were where it was written.
|
|
880
|
+
#
|
|
881
|
+
# out[] = CArray.fuse { (a + b) * (c - a) }
|
|
882
|
+
# total = CArray.fuse { a * weight }.sum
|
|
883
|
+
#
|
|
884
|
+
# What comes back is the expression, not an array: it is computed where
|
|
885
|
+
# it is used -- stored into an array, reduced, or asked for one with
|
|
886
|
+
# `to_ca`. A block holding anything but an expression over arrays comes
|
|
887
|
+
# back as whatever it evaluated to.
|
|
888
|
+
#
|
|
889
|
+
# @return [Object] the expression, as a lazy view
|
|
890
|
+
# @raise [LocalJumpError] when no block is given.
|
|
891
|
+
# @raise [ArgumentError] when the block's source cannot be read -- in
|
|
892
|
+
# irb, in `eval`, or from a file that is gone. Write `.lazy` on the
|
|
893
|
+
# operands there: `a.lazy + b.lazy`.
|
|
894
|
+
def fuse (*args, &block)
|
|
895
|
+
raise LocalJumpError, "CArray.fuse requires a block" unless block
|
|
896
|
+
unless args.empty?
|
|
897
|
+
raise ArgumentError,
|
|
898
|
+
"CArray.fuse takes no arguments: write the expression itself, " \
|
|
899
|
+
"`CArray.fuse { a + b }`, and the names in it are read from " \
|
|
900
|
+
"where the block was written"
|
|
901
|
+
end
|
|
902
|
+
CArray::FuseSource.evaluate(block)
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
# @overload jit_for (*extents) { |i, j, ...| ... }
|
|
906
|
+
# Runs a block over an index space, so that a cell may reach the ones
|
|
907
|
+
# around it -- a recurrence, a stencil. The block is compiled, and the
|
|
908
|
+
# compiler is the carray-jit gem.
|
|
909
|
+
# @raise [NotImplementedError] when that gem is not installed.
|
|
910
|
+
def jit_for (*extents, **options)
|
|
911
|
+
raise NotImplementedError, no_compiler("jit_for")
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
# @overload jit_each { ... }
|
|
915
|
+
# Runs a block over the cells of arrays at once, naming each cell by the
|
|
916
|
+
# array it came from, and writing what it computes into arrays of yours:
|
|
917
|
+
# `CArray.jit_each { out = a + b * c }`. The block is compiled, and the
|
|
918
|
+
# compiler is the carray-jit gem.
|
|
919
|
+
# @raise [NotImplementedError] when that gem is not installed.
|
|
920
|
+
def jit_each (&block)
|
|
921
|
+
raise NotImplementedError, no_compiler("jit_each")
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
# @overload jit_map { ... }
|
|
925
|
+
# The same block with its value asked for: the last statement is what
|
|
926
|
+
# every cell of the result gets, and the result comes back. The block is
|
|
927
|
+
# compiled, and the compiler is the carray-jit gem.
|
|
928
|
+
# @raise [NotImplementedError] when that gem is not installed.
|
|
929
|
+
def jit_map (&block)
|
|
930
|
+
raise NotImplementedError, no_compiler("jit_map")
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
private
|
|
934
|
+
|
|
935
|
+
def no_compiler (name)
|
|
936
|
+
"CArray.#{name} compiles its block, and the compiler is the carray-jit " \
|
|
937
|
+
"gem, which is not installed. An expression over whole arrays can be " \
|
|
938
|
+
"written as `CArray.fuse { ... }` instead, which needs no compiler; " \
|
|
939
|
+
"reaching a neighbouring cell, or writing the loop itself, does."
|
|
940
|
+
end
|
|
941
|
+
end
|