carray 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +5 -25
- data/CHANGELOG.md +16 -0
- data/LICENSE +1 -1
- data/NEWS.md +3 -0
- data/README.md +128 -44
- data/carray.gemspec +22 -24
- data/ext/ca_array_pool.c +91 -0
- data/ext/ca_axis_descriptor.h +186 -0
- data/ext/ca_axis_dispatch.c +924 -0
- data/ext/ca_axis_group.c +1208 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +125 -0
- data/ext/ca_binop_dispatch.h +159 -0
- data/ext/ca_categorical_iterator.c +1375 -0
- data/ext/ca_compare.c +94 -0
- data/ext/ca_compare.h +26 -0
- data/ext/ca_composite_dispatch.c +414 -0
- data/ext/ca_composite_dispatch.h +116 -0
- data/ext/ca_for_buffer.h +96 -0
- data/ext/ca_for_each_element.h +241 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4321 -0
- data/ext/ca_kernel_iterator.h +2603 -0
- data/ext/ca_moncmp_dispatch.c +37 -0
- data/ext/ca_moncmp_dispatch.h +62 -0
- data/ext/ca_monop_dispatch.c +200 -0
- data/ext/ca_monop_dispatch.h +235 -0
- data/ext/ca_obj_array.c +355 -359
- data/ext/ca_obj_bincmp.c +809 -0
- data/ext/ca_obj_binop.c +892 -0
- data/ext/ca_obj_bitarray.c +369 -164
- data/ext/ca_obj_bitfield.c +294 -234
- data/ext/ca_obj_block.c +189 -711
- data/ext/ca_obj_byte_swap.c +766 -0
- data/ext/ca_obj_const_string.c +965 -0
- data/ext/ca_obj_face.c +670 -0
- data/ext/ca_obj_face.h +247 -0
- data/ext/ca_obj_fake.c +228 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +306 -0
- data/ext/ca_obj_grid.c +858 -440
- data/ext/ca_obj_meld.c +1034 -0
- data/ext/ca_obj_moncmp.c +569 -0
- data/ext/ca_obj_monop.c +1111 -0
- data/ext/ca_obj_object.c +774 -298
- data/ext/ca_obj_record.c +468 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +569 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +616 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1296 -0
- data/ext/ca_obj_shift.c +230 -792
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2501 -0
- data/ext/ca_obj_string.c +268 -0
- data/ext/ca_obj_tile.c +614 -0
- data/ext/ca_obj_time.c +546 -0
- data/ext/ca_obj_timedelta.c +435 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +746 -0
- data/ext/ca_obj_unbound_repeat.c +208 -241
- data/ext/ca_obj_window.c +1131 -563
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_ipower.c +319 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +430 -0
- data/ext/ca_sweep_engine.h +157 -0
- data/ext/ca_transform_common.c +228 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +795 -402
- data/ext/carray_access.c +831 -711
- data/ext/carray_attribute.c +98 -330
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +283 -0
- data/ext/carray_call_cfunc.c +1360 -828
- data/ext/carray_call_cfunc.h +160 -0
- data/ext/carray_cast.c +1212 -301
- data/ext/carray_cast_func.rb +81 -40
- data/ext/carray_class.c +53 -63
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +350 -346
- data/ext/carray_copy.c +156 -268
- data/ext/carray_core.c +1342 -199
- data/ext/carray_count.c +312 -0
- data/ext/carray_data_type.c +43 -19
- data/ext/carray_element.c +585 -213
- data/ext/carray_factorize.c +2542 -0
- data/ext/carray_generate.c +230 -559
- data/ext/carray_histogram.c +490 -0
- data/ext/carray_hold.c +228 -0
- data/ext/carray_index_classifier.c +1035 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +120 -0
- data/ext/carray_kernels_bincmp.c +4445 -0
- data/ext/carray_kernels_binop.c +10979 -0
- data/ext/carray_kernels_init.c +36 -0
- data/ext/carray_kernels_map.c +3466 -0
- data/ext/carray_kernels_moncmp.c +2096 -0
- data/ext/carray_kernels_monop.c +18312 -0
- data/ext/carray_kernels_reduce_aggregate.c +25836 -0
- data/ext/carray_kernels_reduce_boolean.c +329 -0
- data/ext/carray_kernels_reduce_cumulative.c +14592 -0
- data/ext/carray_kernels_reduce_extreme.c +16947 -0
- data/ext/carray_kernels_reduce_variance.c +3909 -0
- data/ext/carray_kernels_scan.c +3692 -0
- data/ext/carray_kernels_search.c +32137 -0
- data/ext/carray_kernels_sort.c +10625 -0
- data/ext/carray_kernels_triop.c +1391 -0
- data/ext/carray_lazy.c +567 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +848 -154
- data/ext/carray_math_kernel.h +120 -0
- data/ext/carray_mathfunc.c +10 -241
- data/ext/carray_median_percentile.c +1257 -0
- data/ext/carray_memory_view.c +1625 -0
- data/ext/carray_operator.c +1526 -318
- data/ext/carray_order.c +664 -1394
- data/ext/carray_partition.c +416 -0
- data/ext/carray_random.c +518 -0
- data/ext/carray_scatter.c +357 -0
- data/ext/carray_slab.c +1219 -0
- data/ext/carray_slab.h +84 -0
- data/ext/carray_sort.c +829 -0
- data/ext/carray_sort_kernel.c +620 -0
- data/ext/carray_struct.c +695 -0
- data/ext/carray_test.c +343 -229
- data/ext/carray_undef.c +34 -17
- data/ext/carray_utils.c +175 -74
- data/ext/extconf.rb +216 -55
- data/ext/mk_call_cfunc.rb +480 -0
- data/ext/mkkernel.rb +8842 -0
- data/ext/ruby_carray.c +202 -101
- data/ext/version.h +4 -14
- data/ext/version.rb +5 -13
- data/lib/carray/arrow_tensor.rb +401 -0
- data/lib/carray/attribute.rb +166 -0
- data/lib/carray/autoload_carray.rb +220 -0
- data/lib/carray/autoload_method_extension.rb +44 -0
- data/lib/carray/axis_group.rb +711 -0
- data/lib/carray/basics.rb +481 -0
- data/lib/carray/bincount_nd.rb +358 -0
- data/lib/carray/block_iterator.rb +604 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1062 -0
- data/lib/carray/complex.rb +150 -0
- data/lib/carray/conditional.rb +216 -0
- data/lib/carray/const_string.rb +228 -0
- data/lib/carray/construct.rb +139 -328
- data/lib/carray/core_extensions.rb +240 -0
- data/lib/carray/data_type_extension.rb +233 -0
- data/lib/carray/fixlen_string.rb +95 -0
- data/lib/carray/frame/concat.rb +132 -0
- data/lib/carray/frame/convert.rb +95 -0
- data/lib/carray/frame/csv_parser.rb +211 -0
- data/lib/carray/frame/frame.rb +649 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +164 -0
- data/lib/carray/frame/join.rb +248 -0
- data/lib/carray/frame/records.rb +99 -0
- data/lib/carray/frame/sort.rb +113 -0
- data/lib/carray/frame/verbs.rb +299 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -20
- data/lib/carray/iterator.rb +57 -349
- data/lib/carray/lazy.rb +889 -0
- data/lib/carray/mask_gap_fill.rb +200 -0
- data/lib/carray/math.rb +78 -342
- data/lib/carray/meld_reduce.rb +289 -0
- data/lib/carray/methods/align_addr.rb +116 -0
- data/lib/carray/methods/bin.rb +128 -0
- data/lib/carray/methods/bincount.rb +87 -0
- data/lib/carray/methods/bit_string.rb +92 -0
- data/lib/carray/methods/broadcast.rb +63 -0
- data/lib/carray/methods/choose.rb +39 -0
- data/lib/carray/methods/composition.rb +280 -0
- data/lib/carray/methods/gather_nd.rb +206 -0
- data/lib/carray/methods/index.rb +39 -0
- data/lib/carray/methods/insert_block.rb +99 -0
- data/lib/carray/methods/is_in.rb +141 -0
- data/lib/carray/methods/join.rb +90 -0
- data/lib/carray/methods/locate_addr.rb +47 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +91 -0
- data/lib/carray/methods/mode.rb +126 -0
- data/lib/carray/methods/nunique.rb +46 -0
- data/lib/carray/methods/resize.rb +56 -0
- data/lib/carray/methods/snap.rb +156 -0
- data/lib/carray/methods/string_format.rb +57 -0
- data/lib/carray/methods/unique.rb +47 -0
- data/lib/carray/methods/value_counts.rb +71 -0
- data/lib/carray/mkmf.rb +124 -101
- data/lib/carray/runtime.rb +108 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +292 -0
- data/lib/carray/stack.rb +291 -0
- data/lib/carray/string.rb +56 -180
- data/lib/carray/string_operation_extension.rb +289 -0
- data/lib/carray/struct.rb +335 -323
- data/lib/carray/struct_builder.rb +697 -0
- data/lib/carray/table.rb +41 -2
- data/lib/carray/time.rb +2255 -38
- data/lib/carray/window_iterator.rb +655 -0
- data/lib/carray.rb +55 -57
- metadata +163 -130
- data/Rakefile +0 -51
- data/TODO.md +0 -18
- data/ext/ca_iter_block.c +0 -257
- data/ext/ca_iter_dimension.c +0 -299
- data/ext/ca_iter_window.c +0 -214
- data/ext/ca_obj_mapping.c +0 -644
- data/ext/carray_iterator.c +0 -641
- data/ext/carray_math.rb +0 -850
- data/ext/carray_numeric.c +0 -259
- data/ext/carray_sort_addr.c +0 -254
- data/ext/carray_stat.c +0 -2100
- data/ext/carray_stat_proc.rb +0 -1999
- data/ext/mkmath.rb +0 -741
- data/ext/ruby_ccomplex.c +0 -509
- data/ext/ruby_float_func.c +0 -86
- data/lib/carray/array.rb +0 -8
- data/lib/carray/autoload/autoload_base.rb +0 -19
- data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
- data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
- data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
- data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
- data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
- data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
- data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
- data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
- data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
- data/lib/carray/autoload/autoload_gem_random.rb +0 -8
- data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
- data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
- data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
- data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
- data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
- data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
- data/lib/carray/autoload/autoload_object_link.rb +0 -1
- data/lib/carray/autoload/autoload_object_pack.rb +0 -2
- data/lib/carray/autoload.rb +0 -141
- data/lib/carray/basic.rb +0 -191
- data/lib/carray/broadcast.rb +0 -101
- data/lib/carray/compose.rb +0 -315
- data/lib/carray/convert.rb +0 -115
- data/lib/carray/info.rb +0 -110
- data/lib/carray/io/imagemagick.rb +0 -235
- data/lib/carray/mask.rb +0 -102
- data/lib/carray/math/histogram.rb +0 -177
- data/lib/carray/math/recurrence.rb +0 -93
- data/lib/carray/object/ca_obj_iterator.rb +0 -50
- data/lib/carray/object/ca_obj_link.rb +0 -50
- data/lib/carray/object/ca_obj_pack.rb +0 -99
- data/lib/carray/obsolete.rb +0 -256
- data/lib/carray/ordering.rb +0 -181
- data/lib/carray/testing.rb +0 -51
- data/lib/carray/transform.rb +0 -109
- data/misc/Methods.ja.md +0 -182
- data/misc/NOTE +0 -51
- data/spec/Classes/CABitfield_spec.rb +0 -58
- data/spec/Classes/CABlockIterator_spec.rb +0 -114
- data/spec/Classes/CABlock_spec.rb +0 -205
- data/spec/Classes/CAField_spec.rb +0 -39
- data/spec/Classes/CAGrid_spec.rb +0 -75
- data/spec/Classes/CAMap_spec.rb +0 -0
- data/spec/Classes/CAMapping_spec.rb +0 -105
- data/spec/Classes/CAObject_attribute_spec.rb +0 -33
- data/spec/Classes/CAObject_spec.rb +0 -33
- data/spec/Classes/CARefer_spec.rb +0 -93
- data/spec/Classes/CARepeat_spec.rb +0 -65
- data/spec/Classes/CASelect_spec.rb +0 -22
- data/spec/Classes/CAShift_spec.rb +0 -16
- data/spec/Classes/CAStruct_spec.rb +0 -71
- data/spec/Classes/CATranspose_spec.rb +0 -60
- data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
- data/spec/Classes/CAWindow_spec.rb +0 -54
- data/spec/Classes/CAWrap_spec.rb +0 -8
- data/spec/Classes/CArray_spec.rb +0 -184
- data/spec/Classes/CScalar_spec.rb +0 -55
- data/spec/Classes/ex1.rb +0 -46
- data/spec/Features/feature_130_spec.rb +0 -19
- data/spec/Features/feature_attributes_spec.rb +0 -280
- data/spec/Features/feature_boolean_spec.rb +0 -98
- data/spec/Features/feature_broadcast.rb +0 -116
- data/spec/Features/feature_cast_function.rb +0 -19
- data/spec/Features/feature_cast_spec.rb +0 -33
- data/spec/Features/feature_class_spec.rb +0 -84
- data/spec/Features/feature_complex_spec.rb +0 -42
- data/spec/Features/feature_composite_spec.rb +0 -124
- data/spec/Features/feature_convert_spec.rb +0 -46
- data/spec/Features/feature_copy_spec.rb +0 -123
- data/spec/Features/feature_creation_spec.rb +0 -84
- data/spec/Features/feature_element_spec.rb +0 -144
- data/spec/Features/feature_extream_spec.rb +0 -54
- data/spec/Features/feature_generate_spec.rb +0 -74
- data/spec/Features/feature_index_spec.rb +0 -69
- data/spec/Features/feature_mask_spec.rb +0 -580
- data/spec/Features/feature_math_spec.rb +0 -97
- data/spec/Features/feature_order_spec.rb +0 -146
- data/spec/Features/feature_ref_store_spec.rb +0 -209
- data/spec/Features/feature_serialization_spec.rb +0 -125
- data/spec/Features/feature_stat_spec.rb +0 -397
- data/spec/Features/feature_virtual_spec.rb +0 -48
- data/spec/Features/method_eq_spec.rb +0 -81
- data/spec/Features/method_is_nan_spec.rb +0 -12
- data/spec/Features/method_map_spec.rb +0 -54
- data/spec/Features/method_max_with.rb +0 -20
- data/spec/Features/method_min_with.rb +0 -19
- data/spec/Features/method_ne_spec.rb +0 -18
- data/spec/Features/method_project_spec.rb +0 -188
- data/spec/Features/method_ref_spec.rb +0 -27
- data/spec/Features/method_round_spec.rb +0 -11
- data/spec/Features/method_s_linspace_spec.rb +0 -48
- data/spec/Features/method_s_span_spec.rb +0 -14
- data/spec/Features/method_seq_spec.rb +0 -47
- data/spec/Features/method_sort_with.rb +0 -43
- data/spec/Features/method_sorted_with.rb +0 -29
- data/spec/Features/method_span_spec.rb +0 -42
- data/spec/Features/method_wrap_readonly_spec.rb +0 -43
- data/spec/UnitTest/test_CAVirtual.rb +0 -214
- data/spec/spec_all.rb +0 -10
- data/utils/ca_ase.rb +0 -21
- data/utils/ca_methods.rb +0 -15
- data/utils/cast_checker.rb +0 -30
- data/utils/convert_test.rb +0 -73
- data/utils/extract_yard.rb +0 -22
- data/utils/guess_shape.rb +0 -76
- data/utils/monkey_patch_methods.rb +0 -62
- data/utils/remove_resource_fork.sh +0 -5
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# ----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# carray/core_extensions.rb
|
|
4
|
+
#
|
|
5
|
+
# Refinement that restores the carray-flavoured monkey patches on
|
|
6
|
+
# Float / Integer / Rational / Numeric / TrueClass / FalseClass
|
|
7
|
+
# for files that opt in with `using CArray::CoreExtensions`.
|
|
8
|
+
#
|
|
9
|
+
# Provides:
|
|
10
|
+
# - Postfix math on Float/Integer/Rational (sqrt, exp, log, sin, ...)
|
|
11
|
+
# enabling scalar/CArray polymorphism: the same expression
|
|
12
|
+
# `(0.0415*(t-218.8)).tanh` works whether t is a Float or a CArray.
|
|
13
|
+
# - Angle normalisation on Numeric (deg_360 / deg_180 / rad_2pi / rad_pi).
|
|
14
|
+
# - Comparison helpers on Numeric (#eq / #ne) for symmetric usage:
|
|
15
|
+
# `5.eq(ca)` dispatches to `ca.eq(5)`.
|
|
16
|
+
# - Symmetric bitwise operators on Integer / TrueClass / FalseClass
|
|
17
|
+
# so that `5 | bool_ca`, `0xff & flag_ca`, `1 << position_ca` work
|
|
18
|
+
# without writing CA_INT(5) on the left.
|
|
19
|
+
#
|
|
20
|
+
# None of these are loaded by default `require "carray"`. Each file
|
|
21
|
+
# that wants any of them must opt in:
|
|
22
|
+
#
|
|
23
|
+
# using CArray::CoreExtensions
|
|
24
|
+
#
|
|
25
|
+
# Refinements are lexically scoped to the file (or class/module body)
|
|
26
|
+
# where `using` is written. Methods defined in that scope resolve
|
|
27
|
+
# the refined methods at call time regardless of who is calling.
|
|
28
|
+
#
|
|
29
|
+
# This file is auto-loaded on first reference to CArray::CoreExtensions.
|
|
30
|
+
#
|
|
31
|
+
# ----------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
class CArray
|
|
34
|
+
# Refinement carrying the carray-flavoured additions to Float / Integer /
|
|
35
|
+
# Rational / Numeric / TrueClass / FalseClass, for files that opt in with
|
|
36
|
+
# `using CArray::CoreExtensions`.
|
|
37
|
+
#
|
|
38
|
+
# It provides postfix math on scalars (so `(0.0415*(t-218.8)).tanh` reads
|
|
39
|
+
# the same whether `t` is a Float or a CArray), angle normalisation, and
|
|
40
|
+
# comparison helpers. Being a refinement, it is lexically scoped: nothing
|
|
41
|
+
# changes for code that does not opt in.
|
|
42
|
+
module CoreExtensions
|
|
43
|
+
|
|
44
|
+
# @!visibility private
|
|
45
|
+
MATH_METHODS = %i[
|
|
46
|
+
sqrt exp log log10
|
|
47
|
+
sin cos tan sinh cosh tanh
|
|
48
|
+
asin acos atan asinh acosh atanh
|
|
49
|
+
].freeze
|
|
50
|
+
|
|
51
|
+
[Float, Integer, Rational].each do |klass|
|
|
52
|
+
refine(klass) do
|
|
53
|
+
MATH_METHODS.each do |m|
|
|
54
|
+
define_method(m) { Math.send(m, self) }
|
|
55
|
+
end
|
|
56
|
+
def rad
|
|
57
|
+
self.to_f * Math::PI / 180.0
|
|
58
|
+
end
|
|
59
|
+
def deg
|
|
60
|
+
self.to_f * 180.0 / Math::PI
|
|
61
|
+
end
|
|
62
|
+
def distance(other)
|
|
63
|
+
(self.to_f - other.to_f).abs
|
|
64
|
+
end
|
|
65
|
+
# M.1 (PyTorch alignment) scalar polymorphism additions.
|
|
66
|
+
# `trunc` is left to Ruby's built-in semantics (Float#truncate
|
|
67
|
+
# returns Integer when arg=0) — its CArray counterpart preserves
|
|
68
|
+
# dtype, so scalar-side use should call .to_f.trunc explicitly.
|
|
69
|
+
# Math.expm1 / Math.log1p don't exist in stdlib; hand-roll.
|
|
70
|
+
def expm1
|
|
71
|
+
Math.exp(self) - 1.0
|
|
72
|
+
end
|
|
73
|
+
def log1p
|
|
74
|
+
Math.log(1.0 + self)
|
|
75
|
+
end
|
|
76
|
+
def rsqrt
|
|
77
|
+
1.0 / Math.sqrt(self)
|
|
78
|
+
end
|
|
79
|
+
def square
|
|
80
|
+
self * self
|
|
81
|
+
end
|
|
82
|
+
def signbit
|
|
83
|
+
self.negative?
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
refine Numeric do
|
|
89
|
+
def deg_360
|
|
90
|
+
a = self.to_f
|
|
91
|
+
fa = a / 360.0
|
|
92
|
+
if a >= 0
|
|
93
|
+
(fa - fa.floor) * 360.0
|
|
94
|
+
else
|
|
95
|
+
(fa - fa.ceil + 1) * 360.0
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def deg_180
|
|
100
|
+
a = self.to_f
|
|
101
|
+
fa = (a + 180.0) / 360.0
|
|
102
|
+
b = if a >= 0
|
|
103
|
+
(fa - fa.floor) * 360.0 - 180.0
|
|
104
|
+
else
|
|
105
|
+
(fa - fa.ceil) * 360.0 - 180.0
|
|
106
|
+
end
|
|
107
|
+
b += 360.0 if b <= -180.0
|
|
108
|
+
b
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def rad_2pi
|
|
112
|
+
a = self.to_f
|
|
113
|
+
two_pi = 2 * Math::PI
|
|
114
|
+
fa = a / two_pi
|
|
115
|
+
if a >= 0
|
|
116
|
+
(fa - fa.floor) * two_pi
|
|
117
|
+
else
|
|
118
|
+
(fa - fa.ceil + 1) * two_pi
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def rad_pi
|
|
123
|
+
a = self.to_f
|
|
124
|
+
two_pi = 2 * Math::PI
|
|
125
|
+
fa = (a + Math::PI) / two_pi
|
|
126
|
+
b = if a >= 0
|
|
127
|
+
(fa - fa.floor) * two_pi - Math::PI
|
|
128
|
+
else
|
|
129
|
+
(fa - fa.ceil) * two_pi - Math::PI
|
|
130
|
+
end
|
|
131
|
+
b += two_pi if b <= -Math::PI
|
|
132
|
+
b
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def eq(other)
|
|
136
|
+
if CArray === other
|
|
137
|
+
other.eq(self)
|
|
138
|
+
else
|
|
139
|
+
self == other
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def ne(other)
|
|
144
|
+
if CArray === other
|
|
145
|
+
other.ne(self)
|
|
146
|
+
else
|
|
147
|
+
self != other
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
refine Integer do
|
|
153
|
+
def |(other)
|
|
154
|
+
if CArray === other
|
|
155
|
+
if other.boolean?
|
|
156
|
+
other.bit_or(self)
|
|
157
|
+
else
|
|
158
|
+
l, r = other.coerce(self)
|
|
159
|
+
l | r
|
|
160
|
+
end
|
|
161
|
+
else
|
|
162
|
+
super
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def &(other)
|
|
167
|
+
if CArray === other
|
|
168
|
+
if other.boolean?
|
|
169
|
+
other.bit_and(self)
|
|
170
|
+
else
|
|
171
|
+
l, r = other.coerce(self)
|
|
172
|
+
l & r
|
|
173
|
+
end
|
|
174
|
+
else
|
|
175
|
+
super
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def ^(other)
|
|
180
|
+
if CArray === other
|
|
181
|
+
if other.boolean?
|
|
182
|
+
other.bit_xor(self)
|
|
183
|
+
else
|
|
184
|
+
l, r = other.coerce(self)
|
|
185
|
+
l ^ r
|
|
186
|
+
end
|
|
187
|
+
else
|
|
188
|
+
super
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def <<(other)
|
|
193
|
+
if CArray === other
|
|
194
|
+
l, r = other.coerce(self)
|
|
195
|
+
l << r
|
|
196
|
+
else
|
|
197
|
+
super
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def >>(other)
|
|
202
|
+
if CArray === other
|
|
203
|
+
l, r = other.coerce(self)
|
|
204
|
+
l >> r
|
|
205
|
+
else
|
|
206
|
+
super
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
[TrueClass, FalseClass].each do |klass|
|
|
212
|
+
refine(klass) do
|
|
213
|
+
def |(other)
|
|
214
|
+
if CArray === other && other.boolean?
|
|
215
|
+
other.bit_or(self)
|
|
216
|
+
else
|
|
217
|
+
super
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def &(other)
|
|
222
|
+
if CArray === other && other.boolean?
|
|
223
|
+
other.bit_and(self)
|
|
224
|
+
else
|
|
225
|
+
super
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def ^(other)
|
|
230
|
+
if CArray === other && other.boolean?
|
|
231
|
+
other.bit_xor(self)
|
|
232
|
+
else
|
|
233
|
+
super
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
end # module CoreExtensions
|
|
240
|
+
end # class CArray
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Numo / NumPy-style factory class methods, gathered here as a
|
|
2
|
+
# best-effort soft compatibility layer.
|
|
3
|
+
#
|
|
4
|
+
# Rationale (the apology this file owes the rest of carray):
|
|
5
|
+
#
|
|
6
|
+
# carray's native idiom for building arrays is the typed
|
|
7
|
+
# constructor (CArray.float64(3, 3) { 0 }, CA_INT([1, 2, 3]), and
|
|
8
|
+
# friends). The methods below -- zeros / ones / eye / identity /
|
|
9
|
+
# linspace / arange / full / meshgrid -- duplicate that
|
|
10
|
+
# functionality with NumPy / Numo-flavoured names. They exist
|
|
11
|
+
# for one specific reason: when a user is porting a NumPy or Numo
|
|
12
|
+
# example program over to carray, the first thing they hit is
|
|
13
|
+
# the array-creation entry point, and "I have to learn a whole
|
|
14
|
+
# different API just to call zeros" is the worst place to lose
|
|
15
|
+
# someone. Having these helpers means a paste from
|
|
16
|
+
#
|
|
17
|
+
# np.zeros((3, 3)) -> CArray.zeros([3, 3])
|
|
18
|
+
# np.full((3, 3), 7) -> CArray.full([3, 3], 7)
|
|
19
|
+
# np.linspace(0.0, 1.0, 5) -> CArray.linspace(0.0, 1.0, 5)
|
|
20
|
+
# Numo::DFloat.zeros(3, 3) -> CArray::Float64.zeros(3, 3)
|
|
21
|
+
#
|
|
22
|
+
# works with minimal rewriting. Both the splat-shape carray-ish
|
|
23
|
+
# form (CArray.zeros(3, 3)) and the array-shape NumPy-ish form
|
|
24
|
+
# (CArray.zeros([3, 3])) are accepted by the shape-taking
|
|
25
|
+
# factories.
|
|
26
|
+
#
|
|
27
|
+
# The methods land on CArray itself and on every typed class
|
|
28
|
+
# (CArray::Int32, CArray::Float64, etc.) via the
|
|
29
|
+
# DataTypeExtension module declared (empty) in
|
|
30
|
+
# lib/carray/construct.rb. Required AFTER construct.rb in
|
|
31
|
+
# lib/carray.rb so the module exists -- the extends in construct
|
|
32
|
+
# see this file's method additions transparently.
|
|
33
|
+
#
|
|
34
|
+
# meshgrid was previously in this file; it is now in
|
|
35
|
+
# lib/carray/methods/meshgrid.rb (autoloaded on first call).
|
|
36
|
+
#
|
|
37
|
+
# These helpers are not the "main" carray API. When writing new
|
|
38
|
+
# carray-flavoured code, prefer the typed constructors. Use
|
|
39
|
+
# these when porting external code or when a NumPy / Numo
|
|
40
|
+
# spelling reads more naturally to the audience.
|
|
41
|
+
|
|
42
|
+
class CArray
|
|
43
|
+
|
|
44
|
+
module DataTypeExtension
|
|
45
|
+
|
|
46
|
+
def guess_data_type_from_values (*values)
|
|
47
|
+
if values.all? {|v| v == true || v == false }
|
|
48
|
+
CA_BOOLEAN
|
|
49
|
+
elsif values.all? { |v| v.is_a?(Integer) }
|
|
50
|
+
CA_INT64
|
|
51
|
+
elsif values.all? { |v| v.is_a?(Float) }
|
|
52
|
+
CA_FLOAT64
|
|
53
|
+
elsif values.all? { |v| v.is_a?(Complex) }
|
|
54
|
+
CA_CMPLX128
|
|
55
|
+
else
|
|
56
|
+
CA_OBJECT
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private :guess_data_type_from_values
|
|
61
|
+
|
|
62
|
+
# Normalize a shape argument list: accept both
|
|
63
|
+
#
|
|
64
|
+
# foo(3, 4) # splat (carray-style)
|
|
65
|
+
# foo([3, 4]) # Array (NumPy-style: foo((3, 4)))
|
|
66
|
+
#
|
|
67
|
+
# so users can paste NumPy / Numo example code with minimal
|
|
68
|
+
# rewriting.
|
|
69
|
+
def normalize_shape (args)
|
|
70
|
+
if args.size == 1 && args.first.is_a?(Array)
|
|
71
|
+
args.first
|
|
72
|
+
else
|
|
73
|
+
args
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private :normalize_shape
|
|
78
|
+
|
|
79
|
+
# @overload zeros(*shape)
|
|
80
|
+
# Returns a new CArray of the given shape filled with zeros.
|
|
81
|
+
# Shape accepts both splat Integers and a single Array. When
|
|
82
|
+
# called on a typed class the `data_type` is taken from the
|
|
83
|
+
# class; otherwise it defaults to `CA_FLOAT64`.
|
|
84
|
+
# @param shape [Array<Integer>, Array<Array<Integer>>] shape
|
|
85
|
+
# of the result.
|
|
86
|
+
# @return [CArray]
|
|
87
|
+
def zeros (*args)
|
|
88
|
+
CArray.new(self::DataType || CA_FLOAT64, normalize_shape(args)).zero
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @overload ones(*shape)
|
|
92
|
+
# Returns a new CArray of the given shape filled with ones.
|
|
93
|
+
# Same shape and `data_type` rules as {#zeros}.
|
|
94
|
+
# @param shape [Array<Integer>, Array<Array<Integer>>]
|
|
95
|
+
# @return [CArray]
|
|
96
|
+
def ones (*args)
|
|
97
|
+
CArray.new(self::DataType || CA_FLOAT64, normalize_shape(args)).one
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# @overload eye(n, m = n, k = 0)
|
|
101
|
+
# Returns a 2-D CArray with ones on the `k`-th diagonal and
|
|
102
|
+
# zeros elsewhere.
|
|
103
|
+
# @param n [Integer] number of rows.
|
|
104
|
+
# @param m [Integer] number of columns; defaults to `n`.
|
|
105
|
+
# @param k [Integer] diagonal offset (positive above the main
|
|
106
|
+
# diagonal, negative below).
|
|
107
|
+
# @return [CArray] shape `(n, m)`.
|
|
108
|
+
def eye (n, m = nil, k = 0)
|
|
109
|
+
m ||= n
|
|
110
|
+
mat = CArray.new(self::DataType || CA_FLOAT64, [n, m])
|
|
111
|
+
if k >= 0
|
|
112
|
+
count = [n, m - k].min
|
|
113
|
+
start = k
|
|
114
|
+
else
|
|
115
|
+
count = [n + k, m].min
|
|
116
|
+
start = (-k) * m
|
|
117
|
+
end
|
|
118
|
+
if count > 0
|
|
119
|
+
mat[[start, count, m+1]] = 1
|
|
120
|
+
end
|
|
121
|
+
mat
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# @overload identity(n)
|
|
125
|
+
# Returns the `n` by `n` identity matrix.
|
|
126
|
+
# @param n [Integer] matrix size.
|
|
127
|
+
# @return [CArray] shape `(n, n)`.
|
|
128
|
+
def identity (n)
|
|
129
|
+
mat = CArray.new(self::DataType || CA_FLOAT64, [n, n])
|
|
130
|
+
mat[[nil,n+1]] = 1
|
|
131
|
+
mat
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @overload linspace(x1, x2, n = 100)
|
|
135
|
+
# Returns a 1-D CArray of `n` values evenly spaced from `x1`
|
|
136
|
+
# to `x2` inclusive, matching NumPy's `linspace`. For an integer
|
|
137
|
+
# target `data_type`, values are computed in float64 with the
|
|
138
|
+
# linspace step `(x2 - x1) / (n - 1)`, then floored to the target
|
|
139
|
+
# type — the rule `np.linspace(x1, x2, n, dtype=int)` uses (round
|
|
140
|
+
# toward -infinity, not toward zero). Both endpoints are hit
|
|
141
|
+
# exactly for integer output when `x1` and `x2` are
|
|
142
|
+
# integer-valued.
|
|
143
|
+
#
|
|
144
|
+
# Called on `CArray` itself (module method) integer inputs promote
|
|
145
|
+
# to `CA_FLOAT64` and the result is a float array. Called via an
|
|
146
|
+
# integer subclass (`CArray::Int32.linspace(...)`), the result is
|
|
147
|
+
# an integer array with floor semantics.
|
|
148
|
+
#
|
|
149
|
+
# @param x1 [Numeric] first value.
|
|
150
|
+
# @param x2 [Numeric] last value.
|
|
151
|
+
# @param n [Integer] number of samples.
|
|
152
|
+
# @return [CArray]
|
|
153
|
+
def linspace (x1, x2, n = 100)
|
|
154
|
+
data_type = self::DataType
|
|
155
|
+
unless data_type
|
|
156
|
+
guess = guess_data_type_from_values(x1, x2)
|
|
157
|
+
guess = CA_FLOAT64 if guess == CA_INT64
|
|
158
|
+
data_type = guess
|
|
159
|
+
end
|
|
160
|
+
# span is float-only ("N evenly-spaced integers" is ambiguous —
|
|
161
|
+
# see basics.rb). Route non-float targets through a float64
|
|
162
|
+
# span, then floor + cast; this matches np.linspace(dtype=int)
|
|
163
|
+
# bit for bit (floor rounds toward -infinity so negative
|
|
164
|
+
# midpoints go the same way as numpy's integer linspace).
|
|
165
|
+
float_out = CArray.new(CA_FLOAT64, [n]).span(x1.to_f..x2.to_f)
|
|
166
|
+
return float_out if data_type == CA_FLOAT64
|
|
167
|
+
float_out.floor.to_type(data_type)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# @overload arange(stop)
|
|
171
|
+
# Returns a 1-D CArray with values `0, 1, ..., stop - 1`.
|
|
172
|
+
# @param stop [Numeric] exclusive upper bound.
|
|
173
|
+
# @return [CArray]
|
|
174
|
+
# @overload arange(start, stop)
|
|
175
|
+
# Returns `start, start + 1, ..., stop - 1`.
|
|
176
|
+
# @param start [Numeric] inclusive lower bound.
|
|
177
|
+
# @param stop [Numeric] exclusive upper bound.
|
|
178
|
+
# @return [CArray]
|
|
179
|
+
# @overload arange(start, stop, step)
|
|
180
|
+
# Returns values from `start` to `stop` (exclusive) stepping
|
|
181
|
+
# by `step`.
|
|
182
|
+
# @param start [Numeric] inclusive lower bound.
|
|
183
|
+
# @param stop [Numeric] exclusive upper bound.
|
|
184
|
+
# @param step [Numeric] spacing.
|
|
185
|
+
# @return [CArray]
|
|
186
|
+
def arange (*args)
|
|
187
|
+
case args.size
|
|
188
|
+
when 3
|
|
189
|
+
start, stop, step = *args
|
|
190
|
+
when 2
|
|
191
|
+
start, stop = *args
|
|
192
|
+
step = 1
|
|
193
|
+
when 1
|
|
194
|
+
start = 0
|
|
195
|
+
stop, = *args
|
|
196
|
+
step = 1
|
|
197
|
+
end
|
|
198
|
+
data_type = self::DataType
|
|
199
|
+
data_type ||= guess_data_type_from_values(start, stop, step)
|
|
200
|
+
CArray.send(:__cast__, data_type, start..stop-step, step)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# @overload full(shape, fill_value)
|
|
204
|
+
# Returns a new CArray of the given shape filled with
|
|
205
|
+
# `fill_value`. `shape` accepts an Integer or an Array of
|
|
206
|
+
# Integers. The `data_type` is inferred from `fill_value`
|
|
207
|
+
# when the receiver is not a typed class.
|
|
208
|
+
# @param shape [Integer, Array<Integer>]
|
|
209
|
+
# @param fill_value [Object] value used to fill the array.
|
|
210
|
+
# @return [CArray]
|
|
211
|
+
def full (shape, fill_value)
|
|
212
|
+
data_type = self::DataType
|
|
213
|
+
data_type ||= guess_data_type_from_values(fill_value)
|
|
214
|
+
shape = [shape] unless shape.is_a?(Array)
|
|
215
|
+
CArray.new(data_type, shape).fill(fill_value)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# @overload empty(*shape)
|
|
219
|
+
# Returns a new CArray of the given shape whose contents are
|
|
220
|
+
# **uninitialised**. The caller must overwrite the array
|
|
221
|
+
# before reading from it. `CA_OBJECT` silently falls back to
|
|
222
|
+
# a zero-VALUE init required for GC safety.
|
|
223
|
+
# @param shape [Array<Integer>, Array<Array<Integer>>]
|
|
224
|
+
# @return [CArray]
|
|
225
|
+
def empty (*args)
|
|
226
|
+
CArray.__alloc_uninit__(self::DataType || CA_FLOAT64,
|
|
227
|
+
normalize_shape(args))
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
end
|
|
233
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# ----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# CAFixlenString high-level construction surface.
|
|
4
|
+
#
|
|
5
|
+
# CAFixlenString itself (the string interpretation of CA_FIXLEN storage) lives
|
|
6
|
+
# in ext/ca_obj_string.c. This file provides the ergonomic builder that packs
|
|
7
|
+
# Ruby Strings into a fixed-width CA_FIXLEN entity and wraps it via
|
|
8
|
+
# CAFixlenString.wrap.
|
|
9
|
+
#
|
|
10
|
+
# ----------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
class CArray
|
|
13
|
+
|
|
14
|
+
# Build a CAFixlenString (fixed-width String array over CA_FIXLEN storage).
|
|
15
|
+
#
|
|
16
|
+
# CArray.fixlen_string(["ab", "cde"], bytes: 4) # explicit slot width
|
|
17
|
+
# CArray.fixlen_string(["ab", "cde"]) # width = max bytesize
|
|
18
|
+
# CArray.fixlen_string([a, nil, b], bytes: 8) # nil → masked element
|
|
19
|
+
#
|
|
20
|
+
# The bounded slot width is the storage seam CAFixlenString exposes.
|
|
21
|
+
# `truncate:` controls what happens when
|
|
22
|
+
# a value exceeds `bytes` (only reachable when `bytes` is given explicitly;
|
|
23
|
+
# the auto width can never overflow):
|
|
24
|
+
#
|
|
25
|
+
# :error (default) raise ArgumentError on overflow — loud data loss
|
|
26
|
+
# :silent let the native fixlen store keep the leading `bytes` bytes
|
|
27
|
+
#
|
|
28
|
+
# The overflow policy lives at this construction surface, not at per-cell
|
|
29
|
+
# `fix[i] = v` (which always truncates silently via the native fixlen store).
|
|
30
|
+
# A CArray source is normalised through a String Face
|
|
31
|
+
# ({CArray.string_face_of}): a raw CA_FIXLEN of matching width wraps
|
|
32
|
+
# zero-copy, other string-bearing arrays materialise, numeric is rejected.
|
|
33
|
+
# @overload fixlen_string(values, bytes: nil, truncate: :error)
|
|
34
|
+
# @param values [Array<String, nil>] source values.
|
|
35
|
+
# @param bytes [Integer, nil] slot width; defaults to the max bytesize.
|
|
36
|
+
# @param truncate [Symbol] `:error` or `:silent`.
|
|
37
|
+
# @return [CAFixlenString]
|
|
38
|
+
# @overload fixlen_string(ca, bytes: nil, truncate: :error)
|
|
39
|
+
# @param ca [CArray] a String Face, CA_OBJECT, or raw CA_FIXLEN array.
|
|
40
|
+
# @return [CAFixlenString]
|
|
41
|
+
# @raise [CArray::DataTypeError] if `ca` is numeric / boolean.
|
|
42
|
+
# @overload fixlen_string(n, bytes: nil, truncate: :error) { |i| ... }
|
|
43
|
+
# @param n [Integer] element count.
|
|
44
|
+
# @param bytes [Integer, nil] slot width; defaults to the max bytesize.
|
|
45
|
+
# @param truncate [Symbol] `:error` or `:silent`.
|
|
46
|
+
# @return [CAFixlenString]
|
|
47
|
+
def self.fixlen_string (arg, bytes: nil, truncate: :error, &block)
|
|
48
|
+
if arg.is_a?(CArray)
|
|
49
|
+
return string_face_of(arg).to_fixlen_string(bytes: bytes, truncate: truncate)
|
|
50
|
+
end
|
|
51
|
+
unless [:error, :silent].include?(truncate)
|
|
52
|
+
raise ArgumentError, "truncate: must be :error or :silent (got #{truncate.inspect})"
|
|
53
|
+
end
|
|
54
|
+
if block
|
|
55
|
+
n = Integer(arg)
|
|
56
|
+
if block.arity == 0
|
|
57
|
+
v = block.call
|
|
58
|
+
values = Array.new(n) { v }
|
|
59
|
+
else
|
|
60
|
+
values = Array.new(n) { |i| block.call(i) }
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
values = arg.to_a
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
width = bytes || values.compact.map { |s| s.to_s.bytesize }.max || 1
|
|
67
|
+
width = 1 if width < 1
|
|
68
|
+
|
|
69
|
+
if truncate == :error
|
|
70
|
+
values.each_with_index do |s, i|
|
|
71
|
+
next if s.nil?
|
|
72
|
+
b = s.to_s.bytesize
|
|
73
|
+
if b > width
|
|
74
|
+
raise ArgumentError,
|
|
75
|
+
"CArray.fixlen_string: value at #{i} is #{b} bytes, exceeds slot width #{width} " \
|
|
76
|
+
"(use truncate: :silent to keep the leading bytes)"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
entity = CArray.new(CA_FIXLEN, [values.size], :bytes => width)
|
|
82
|
+
values.each_with_index do |s, i|
|
|
83
|
+
entity[i] = s.nil? ? UNDEF : s.to_s
|
|
84
|
+
end
|
|
85
|
+
CAFixlenString.wrap(entity)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Mutable fixed-width String Face: L1 ops + in-place variants. In-place
|
|
91
|
+
# transforms re-store through the native fixlen path (silent truncate).
|
|
92
|
+
class CAFixlenString
|
|
93
|
+
include CArray::StringOperationMixin
|
|
94
|
+
include CArray::StringOperationMixin::Mutable
|
|
95
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# CAFrame row concatenation (memo §5, spine = addressing + gather).
|
|
2
|
+
#
|
|
3
|
+
# Two entry points, mirroring the CArray-level taxonomy:
|
|
4
|
+
#
|
|
5
|
+
# +CAFrame.meld+ view frame, strict same-dtype per column,
|
|
6
|
+
# each column is a CAMeld view over the inputs
|
|
7
|
+
# +CAFrame.concatenate+ eager frame, auto-casts per column,
|
|
8
|
+
# each column is a materialised entity
|
|
9
|
+
#
|
|
10
|
+
# Both are class methods (symmetric N-ary; no frame is privileged), like
|
|
11
|
+
# +CArray.meld+ / +CArray.concatenate+, not instance verbs.
|
|
12
|
+
|
|
13
|
+
class CAFrame
|
|
14
|
+
# Weld frames along the row axis, view-style. Each output column is
|
|
15
|
+
# +CArray.meld+ of that column across the input frames, so the result
|
|
16
|
+
# is a view frame that shares storage with the inputs (chain composability
|
|
17
|
+
# preserved: writes to the result flow back to whichever input frame owns
|
|
18
|
+
# the target segment, and vice versa).
|
|
19
|
+
#
|
|
20
|
+
# Per-column +data_type+ must match across frames (CArray.meld is a view
|
|
21
|
+
# constructor that refuses to auto-cast — silent promotion here would
|
|
22
|
+
# hide schema drift). For an eager, auto-casting alternative use
|
|
23
|
+
# {CAFrame.concatenate}.
|
|
24
|
+
#
|
|
25
|
+
# Column matching is by name (output order follows the first frame);
|
|
26
|
+
# every frame must carry the same column-name set. N-D columns carry
|
|
27
|
+
# their trailing dimensions along; masks are preserved.
|
|
28
|
+
#
|
|
29
|
+
# The index is welded too when every frame has one (their +axis_name+
|
|
30
|
+
# must agree); if none do, the result has no index; a mix raises.
|
|
31
|
+
# Column-set mismatch raises — a union-with-UNDEF mode is a possible
|
|
32
|
+
# future opt-in, kept out here to stay explicit (memo §4.2).
|
|
33
|
+
#
|
|
34
|
+
# CAFrame.meld(jan, feb, mar) # view over three months
|
|
35
|
+
# CAFrame.meld([jan, feb, mar]) # an Array is accepted too
|
|
36
|
+
def self.meld(*frames)
|
|
37
|
+
frames = frames.flatten
|
|
38
|
+
check_concat_inputs(frames, verb: "meld")
|
|
39
|
+
first = frames.first
|
|
40
|
+
names = first.variable_names
|
|
41
|
+
check_column_sets(frames, names, verb: "meld")
|
|
42
|
+
cols = {}
|
|
43
|
+
names.each do |name|
|
|
44
|
+
cols[name] = CArray.meld(frames.map { |f| f[name] }, axis: 0)
|
|
45
|
+
end
|
|
46
|
+
new(cols, axis_name: first.axis_name, index: meld_index(frames))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Concatenate frames along the row axis, eagerly. Each output column is
|
|
50
|
+
# +CArray.concatenate+ of that column across the input frames, so per-column
|
|
51
|
+
# dtypes auto-promote to a common type. The result is a fresh, independent
|
|
52
|
+
# frame — writes to it do not propagate back to the input frames.
|
|
53
|
+
#
|
|
54
|
+
# For a view frame that shares storage with the inputs (strict same dtype
|
|
55
|
+
# per column, chain composability preserved) use {CAFrame.meld}.
|
|
56
|
+
#
|
|
57
|
+
# Column matching, index handling, and column-set / index-mix rules
|
|
58
|
+
# match {CAFrame.meld}.
|
|
59
|
+
#
|
|
60
|
+
# CAFrame.concatenate(jan, feb, mar) # eager, independent result
|
|
61
|
+
# CAFrame.concatenate([jan, feb, mar]) # an Array is accepted too
|
|
62
|
+
def self.concatenate(*frames)
|
|
63
|
+
frames = frames.flatten
|
|
64
|
+
check_concat_inputs(frames, verb: "concatenate")
|
|
65
|
+
first = frames.first
|
|
66
|
+
names = first.variable_names
|
|
67
|
+
check_column_sets(frames, names, verb: "concatenate")
|
|
68
|
+
cols = {}
|
|
69
|
+
names.each do |name|
|
|
70
|
+
cols[name] = CArray.concatenate(frames.map { |f| f[name] })
|
|
71
|
+
end
|
|
72
|
+
new(cols, axis_name: first.axis_name, index: concatenate_index(frames))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# ---- shared validators -------------------------------------------------
|
|
76
|
+
|
|
77
|
+
def self.check_concat_inputs(frames, verb:)
|
|
78
|
+
raise ArgumentError, "#{verb} requires at least one frame" if frames.empty?
|
|
79
|
+
unless frames.all? { |f| f.is_a?(CAFrame) }
|
|
80
|
+
raise ArgumentError, "#{verb} expects CAFrame arguments"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
private_class_method :check_concat_inputs
|
|
84
|
+
|
|
85
|
+
def self.check_column_sets(frames, names, verb:)
|
|
86
|
+
expected = names.sort
|
|
87
|
+
frames.each_with_index do |f, i|
|
|
88
|
+
next if i.zero?
|
|
89
|
+
if f.variable_names.sort != expected
|
|
90
|
+
raise ArgumentError,
|
|
91
|
+
"#{verb}: frame #{i} has columns #{f.variable_names.inspect}, " \
|
|
92
|
+
"expected the same set as #{names.inspect}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
private_class_method :check_column_sets
|
|
97
|
+
|
|
98
|
+
# ---- index helpers -----------------------------------------------------
|
|
99
|
+
|
|
100
|
+
# Index welded via CArray.meld (view). Every frame must agree: all
|
|
101
|
+
# indexed (axis names must match) or none.
|
|
102
|
+
def self.meld_index(frames)
|
|
103
|
+
indexes, axis = index_pieces(frames, verb: "meld")
|
|
104
|
+
return nil unless indexes
|
|
105
|
+
axis # unused; kept for symmetry
|
|
106
|
+
CArray.meld(indexes, axis: 0)
|
|
107
|
+
end
|
|
108
|
+
private_class_method :meld_index
|
|
109
|
+
|
|
110
|
+
# Index concatenated eagerly via CArray.concatenate.
|
|
111
|
+
def self.concatenate_index(frames)
|
|
112
|
+
indexes, _axis = index_pieces(frames, verb: "concatenate")
|
|
113
|
+
return nil unless indexes
|
|
114
|
+
CArray.concatenate(indexes)
|
|
115
|
+
end
|
|
116
|
+
private_class_method :concatenate_index
|
|
117
|
+
|
|
118
|
+
def self.index_pieces(frames, verb:)
|
|
119
|
+
indexes = frames.map(&:index)
|
|
120
|
+
return nil if indexes.none?
|
|
121
|
+
unless indexes.all?
|
|
122
|
+
raise ArgumentError, "#{verb}: some frames have an index and others do not"
|
|
123
|
+
end
|
|
124
|
+
axis_names = frames.map(&:axis_name).uniq
|
|
125
|
+
unless axis_names.size == 1
|
|
126
|
+
raise ArgumentError,
|
|
127
|
+
"#{verb}: indexed frames have different axis names #{axis_names.inspect}"
|
|
128
|
+
end
|
|
129
|
+
[indexes, axis_names.first]
|
|
130
|
+
end
|
|
131
|
+
private_class_method :index_pieces
|
|
132
|
+
end
|