carray 3.0.0 → 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 +2 -1
- data/CHANGELOG.md +325 -3
- data/{NEWS.md → CHANGELOG.v1.md} +1 -1
- data/README.md +32 -32
- data/carray.gemspec +12 -8
- data/ext/ca_axis_group.c +55 -19
- data/ext/ca_binop_dispatch.c +5 -6
- data/ext/ca_binop_dispatch.h +0 -7
- data/ext/ca_categorical_iterator.c +13 -13
- data/ext/ca_for_each_element.h +6 -8
- data/ext/ca_group_iter.c +1 -1
- data/ext/ca_kernel_iterator.c +69 -23
- data/ext/ca_kernel_iterator.h +3 -10
- data/ext/ca_obj_bincmp.c +57 -27
- data/ext/ca_obj_binop.c +86 -30
- data/ext/ca_obj_const_string.c +3 -1
- data/ext/ca_obj_face.c +80 -0
- data/ext/ca_obj_face.h +33 -1
- data/ext/ca_obj_fake.c +11 -0
- data/ext/ca_obj_fixlen_string.c +3 -1
- data/ext/ca_obj_grid.c +11 -3
- data/ext/ca_obj_meld.c +13 -8
- data/ext/ca_obj_moncmp.c +33 -14
- data/ext/ca_obj_monop.c +19 -7
- data/ext/ca_obj_object.c +99 -4
- data/ext/ca_obj_record.c +3 -1
- data/ext/ca_obj_refer.c +30 -6
- data/ext/ca_obj_roll.c +13 -5
- data/ext/ca_obj_select_axis.c +16 -6
- data/ext/ca_obj_shift.c +3 -3
- data/ext/ca_obj_stride.c +106 -23
- data/ext/ca_obj_string.c +3 -1
- data/ext/ca_obj_tile.c +13 -5
- data/ext/ca_obj_time.c +3 -1
- data/ext/ca_obj_timedelta.c +3 -1
- data/ext/ca_obj_transpose.c +1 -1
- data/ext/ca_obj_triop.c +62 -23
- data/ext/ca_obj_window.c +86 -17
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +0 -3
- data/ext/ca_sort_kernels.h +5 -5
- data/ext/ca_sweep_engine.c +78 -35
- data/ext/ca_sweep_engine.h +13 -4
- data/ext/ca_transform_common.c +7 -0
- data/ext/carray.h +127 -130
- data/ext/carray_access.c +56 -34
- data/ext/carray_bincount.c +8 -8
- data/ext/carray_broadcast.c +99 -6
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +1491 -0
- data/ext/carray_call_cfunc.h +153 -0
- data/ext/carray_cast.c +69 -31
- data/ext/carray_conversion.c +22 -22
- data/ext/carray_copy.c +12 -2
- data/ext/carray_core.c +57 -10
- data/ext/carray_count.c +1 -1
- data/ext/carray_element.c +7 -7
- data/ext/carray_factorize.c +28 -28
- data/ext/carray_hold.c +1 -1
- data/ext/carray_index_classifier.c +7 -21
- data/ext/carray_internal.h +19 -3
- data/ext/carray_kernels_bincmp.c +1 -0
- data/ext/carray_kernels_binop.c +320 -298
- data/ext/carray_kernels_init.c +1095 -0
- data/ext/carray_kernels_map.c +1 -0
- data/ext/carray_kernels_moncmp.c +1 -0
- data/ext/carray_kernels_monop.c +412 -411
- data/ext/carray_kernels_reduce_aggregate.c +88 -87
- data/ext/carray_kernels_reduce_boolean.c +1 -0
- data/ext/carray_kernels_reduce_cumulative.c +54 -53
- data/ext/carray_kernels_reduce_extreme.c +51 -50
- data/ext/carray_kernels_reduce_variance.c +1 -0
- data/ext/carray_kernels_scan.c +1 -0
- data/ext/carray_kernels_search.c +93 -92
- data/ext/carray_kernels_sort.c +1 -0
- data/ext/carray_kernels_triop.c +1 -0
- data/ext/carray_lazy.c +175 -5
- data/ext/carray_mask.c +6 -5
- data/ext/carray_median_percentile.c +1 -1
- data/ext/carray_memory_view.c +46 -21
- data/ext/carray_operator.c +46 -49
- data/ext/carray_partition.c +4 -4
- data/ext/carray_random.c +7 -7
- data/ext/carray_scatter.c +1 -1
- data/ext/carray_sort.c +3 -3
- data/ext/carray_sort_kernel.c +10 -10
- data/ext/carray_test.c +1 -1
- data/ext/extconf.rb +18 -0
- data/ext/mk_call_cfunc.rb +243 -52
- data/ext/mkkernel.rb +354 -100
- data/ext/ruby_carray.c +18 -16
- data/ext/version.h +4 -4
- data/lib/carray/autoload_carray.rb +19 -0
- data/lib/carray/autoload_method_extension.rb +2 -1
- data/lib/carray/axis_group.rb +7 -7
- data/lib/carray/basics.rb +59 -59
- data/lib/carray/bincount_nd.rb +12 -12
- data/lib/carray/block_iterator.rb +24 -14
- data/lib/carray/categorical.rb +4 -4
- data/lib/carray/categorical_iterator.rb +98 -76
- data/lib/carray/conditional.rb +14 -14
- data/lib/carray/construct.rb +21 -0
- data/lib/carray/core_extensions.rb +62 -5
- data/lib/carray/data_type_extension.rb +18 -1
- data/lib/carray/frame/concat.rb +3 -3
- data/lib/carray/frame/convert.rb +1 -1
- data/lib/carray/frame/frame.rb +4 -11
- data/lib/carray/frame/io.rb +159 -2
- data/lib/carray/frame/sort.rb +1 -1
- data/lib/carray/frame/verbs.rb +18 -1
- data/lib/carray/fuse_source.rb +123 -0
- data/lib/carray/fusion.rb +218 -0
- data/lib/carray/histogram.rb +16 -16
- data/lib/carray/inspect.rb +1 -7
- data/lib/carray/iterator.rb +4 -3
- data/lib/carray/lazy.rb +125 -73
- data/lib/carray/meld_reduce.rb +2 -2
- data/lib/carray/methods/align_addr.rb +1 -1
- data/lib/carray/methods/composition.rb +1 -1
- data/lib/carray/methods/is_in.rb +12 -12
- data/lib/carray/methods/locate_addr.rb +6 -1
- data/lib/carray/methods/mask_duplicates.rb +1 -1
- data/lib/carray/methods/meshgrid.rb +4 -5
- data/lib/carray/methods/mode.rb +2 -2
- data/lib/carray/methods/nunique.rb +1 -1
- data/lib/carray/methods/snap.rb +7 -2
- data/lib/carray/methods/unique.rb +3 -3
- data/lib/carray/methods/value_counts.rb +2 -2
- data/lib/carray/runtime.rb +0 -19
- data/lib/carray/slab_iterator.rb +20 -7
- data/lib/carray/string_operation_extension.rb +5 -5
- data/lib/carray/time.rb +890 -491
- data/lib/carray/window_iterator.rb +285 -13
- data/lib/carray.rb +5 -5
- 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 +59 -10
- data/ext/ca_obj_unbound_repeat.c +0 -496
data/ext/ruby_carray.c
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Single entry point Init_carray_ext: declares the CArray class
|
|
4
4
|
hierarchy + exception class + CA module, populates top-level
|
|
5
|
-
constants (CA_RANK_MAX /
|
|
5
|
+
constants (CA_RANK_MAX / CA_* data_type Symbols / alignment
|
|
6
6
|
values), then drives the per-module Init_* sequence in load order.
|
|
7
7
|
|
|
8
8
|
CAREFUL: the Init_* call order below carries real ordering
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
---------------------------------------------------------------------------- */
|
|
15
15
|
|
|
16
16
|
#include "carray.h"
|
|
17
|
+
#include "carray_build_flags.h"
|
|
17
18
|
#include "version.h"
|
|
18
19
|
|
|
19
20
|
#ifdef HAVE_RB_ARITHMETIC_SEQUENCE_EXTRACT
|
|
@@ -26,7 +27,7 @@ VALUE rb_cCAIterator;
|
|
|
26
27
|
VALUE rb_eCADataTypeError;
|
|
27
28
|
VALUE rb_mCA;
|
|
28
29
|
|
|
29
|
-
VALUE
|
|
30
|
+
VALUE CA_UNSPECIFIED;
|
|
30
31
|
|
|
31
32
|
void Init_carray_core ();
|
|
32
33
|
void Init_carray_undef ();
|
|
@@ -81,7 +82,6 @@ void Init_ca_obj_stack (); /* multi-parent outer-axis stack */
|
|
|
81
82
|
void Init_ca_obj_meld (); /* multi-parent ragged concat along existing axis */
|
|
82
83
|
void Init_ca_obj_transpose ();
|
|
83
84
|
void Init_ca_obj_repeat ();
|
|
84
|
-
void Init_ca_obj_unbound_repeat ();
|
|
85
85
|
void Init_ca_obj_reduce ();
|
|
86
86
|
void Init_ca_obj_field ();
|
|
87
87
|
void Init_ca_obj_fake ();
|
|
@@ -107,7 +107,7 @@ void Init_ca_obj_moncmp (); /* CAMonCmp + lazy elementwise predicate dispatch *
|
|
|
107
107
|
|
|
108
108
|
/* Init_carray_iterator retired; CAIterator is a form-only base defined inline
|
|
109
109
|
in Init_carray_ext (accessors in lib/carray/iterator.rb). The 2.0 C engines
|
|
110
|
-
(dimension / window / block iterators)
|
|
110
|
+
(dimension / window / block iterators) were retired in 3.0. */
|
|
111
111
|
|
|
112
112
|
void Init_carray_mathfunc ();
|
|
113
113
|
|
|
@@ -142,7 +142,7 @@ Init_carray_ext (void)
|
|
|
142
142
|
rb_cCScalar = rb_define_class("CScalar", rb_cCArray);
|
|
143
143
|
rb_cCAView = rb_define_class("CAView", rb_cCArray);
|
|
144
144
|
/* CAREFUL: CAStride must exist before its subclasses (CARefer /
|
|
145
|
-
CABlock / CAField / CARepeat /
|
|
145
|
+
CABlock / CAField / CARepeat / CATranspose /
|
|
146
146
|
CAFarray) are defined or registered. See devel/CAStride.md. */
|
|
147
147
|
rb_cCAStride = rb_define_class("CAStride", rb_cCAView);
|
|
148
148
|
rb_cCARefer = rb_define_class("CARefer", rb_cCAStride);
|
|
@@ -151,10 +151,6 @@ Init_carray_ext (void)
|
|
|
151
151
|
rb_cCASelect = rb_define_class("CASelect", rb_cCAView);
|
|
152
152
|
rb_cCAObject = rb_define_class("CAObject", rb_cCAView);
|
|
153
153
|
rb_cCARepeat = rb_define_class("CARepeat", rb_cCAStride);
|
|
154
|
-
/* CAUnboundRepeat: a CAStride with stride-0 axes on each `*` position
|
|
155
|
-
and sized (matching parent) on each nil position. */
|
|
156
|
-
rb_cCAUnboundRepeat = rb_define_class("CAUnboundRepeat", rb_cCAStride);
|
|
157
|
-
|
|
158
154
|
rb_cCArrayMask = rb_define_class("CArrayMask", rb_cCArray);
|
|
159
155
|
rb_cCAStrideMask = rb_define_class("CAStrideMask", rb_cCAStride);
|
|
160
156
|
rb_cCAReferMask = rb_define_class("CAReferMask", rb_cCARefer);
|
|
@@ -162,7 +158,6 @@ Init_carray_ext (void)
|
|
|
162
158
|
rb_cCAFieldMask = rb_define_class("CAFieldMask", rb_cCAField);
|
|
163
159
|
rb_cCASelectMask = rb_define_class("CASelectMask", rb_cCASelect);
|
|
164
160
|
rb_cCARepeatMask = rb_define_class("CARepeatMask", rb_cCARepeat);
|
|
165
|
-
rb_cCAUnboundRepeatMask = rb_define_class("CAUnboundRepeatMask", rb_cCAUnboundRepeat);
|
|
166
161
|
|
|
167
162
|
/* -- Exception class -- */
|
|
168
163
|
|
|
@@ -175,6 +170,10 @@ Init_carray_ext (void)
|
|
|
175
170
|
|
|
176
171
|
/* -- version -- */
|
|
177
172
|
|
|
173
|
+
/* The flags this build's arithmetic was compiled with. Anything that
|
|
174
|
+
computes what these kernels compute, somewhere other than in them, has
|
|
175
|
+
to be built the same way to arrive at the same answer. */
|
|
176
|
+
rb_define_const(rb_cCArray, "BUILD_FLAGS", rb_str_new2(CA_BUILD_FLAGS));
|
|
178
177
|
rb_define_const(rb_cCArray, "VERSION", rb_str_new2(CA_VERSION));
|
|
179
178
|
rb_define_const(rb_cCArray, "VERSION_CODE", INT2NUM(CA_VERSION_CODE));
|
|
180
179
|
rb_define_const(rb_cCArray, "VERSION_MAJOR", INT2NUM(CA_VERSION_MAJOR));
|
|
@@ -184,8 +183,14 @@ Init_carray_ext (void)
|
|
|
184
183
|
|
|
185
184
|
/* -- system -- */
|
|
186
185
|
rb_define_const(rb_cObject, "CA_RANK_MAX", INT2NUM(CA_RANK_MAX));
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
/* Sentinel meaning "the caller did not give this argument". It is
|
|
187
|
+
distinct from nil because nil is itself a legal fill value (a
|
|
188
|
+
CA_OBJECT array can be filled with nil), so nil cannot mark
|
|
189
|
+
absence. C code only ever compares against it; the value is never
|
|
190
|
+
read and must never be passed in from Ruby. The constant exists to
|
|
191
|
+
anchor the object against the GC. */
|
|
192
|
+
CA_UNSPECIFIED = rb_funcall(rb_cObject, rb_intern("new"), 0);
|
|
193
|
+
rb_define_const(rb_cCArray, "UNSPECIFIED", CA_UNSPECIFIED);
|
|
189
194
|
|
|
190
195
|
#ifdef HAVE_COMPLEX_H
|
|
191
196
|
/* @private */
|
|
@@ -320,7 +325,6 @@ Init_carray_ext (void)
|
|
|
320
325
|
Init_ca_obj_meld();
|
|
321
326
|
Init_ca_obj_transpose();
|
|
322
327
|
Init_ca_obj_repeat();
|
|
323
|
-
Init_ca_obj_unbound_repeat();
|
|
324
328
|
Init_ca_obj_reduce();
|
|
325
329
|
Init_ca_obj_field();
|
|
326
330
|
Init_ca_obj_fake();
|
|
@@ -351,9 +355,7 @@ Init_carray_ext (void)
|
|
|
351
355
|
/* CAIterator is the form-only base of the 3.0 iterator family; its shared
|
|
352
356
|
accessors are plain Ruby attr_readers (lib/carray/iterator.rb). The C
|
|
353
357
|
reduction iterators subclass it and carray_access.c constructs
|
|
354
|
-
CASlabIterator by name, so the class object must exist here.
|
|
355
|
-
2.0 dispatch engines (dimension / window / block C iterators + the
|
|
356
|
-
kernel_at_addr base) are preserved at samples/caiterator/. */
|
|
358
|
+
CASlabIterator by name, so the class object must exist here. */
|
|
357
359
|
rb_cCAIterator = rb_define_class("CAIterator", rb_cObject);
|
|
358
360
|
|
|
359
361
|
Init_ca_group_iter(); /* devel/MEMO_AXIS_GROUP.md — axis-group [] surface
|
data/ext/version.h
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#define CA_VERSION "3.0.
|
|
2
|
-
#define CA_VERSION_CODE
|
|
1
|
+
#define CA_VERSION "3.0.1"
|
|
2
|
+
#define CA_VERSION_CODE 301
|
|
3
3
|
#define CA_VERSION_MAJOR 3
|
|
4
4
|
#define CA_VERSION_MINOR 0
|
|
5
|
-
#define CA_VERSION_TEENY
|
|
6
|
-
#define CA_VERSION_DATE "2026/
|
|
5
|
+
#define CA_VERSION_TEENY 1
|
|
6
|
+
#define CA_VERSION_DATE "2026/09/07"
|
|
@@ -62,6 +62,24 @@ class CArray
|
|
|
62
62
|
autoload :TableMethods, "carray/table"
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
+
# ---- Fusion (reading a lazy expression as a plan) --------------------------
|
|
66
|
+
#
|
|
67
|
+
# Nothing in the core asks for a plan -- CArray can always walk the view --
|
|
68
|
+
# so this loads only when something that compiles one asks.
|
|
69
|
+
|
|
70
|
+
class CArray
|
|
71
|
+
autoload :Fusion, "carray/fusion"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# ---- Reading a block as an expression (CArray.fuse) -------------------------
|
|
75
|
+
#
|
|
76
|
+
# Only `fuse` needs this, and only when it is called, so the parser it uses
|
|
77
|
+
# is not loaded by `require "carray"`.
|
|
78
|
+
|
|
79
|
+
class CArray
|
|
80
|
+
autoload :FuseSource, "carray/fuse_source"
|
|
81
|
+
end
|
|
82
|
+
|
|
65
83
|
# ---- Iterators (class / slab) ----------------------------------------------
|
|
66
84
|
|
|
67
85
|
autoload :CASlabIterator, "carray/slab_iterator"
|
|
@@ -172,6 +190,7 @@ end
|
|
|
172
190
|
# (2) Single-feature method files under carray/methods/
|
|
173
191
|
# ============================================================================
|
|
174
192
|
|
|
193
|
+
|
|
175
194
|
class CArray
|
|
176
195
|
# composition family (eager ragged list -> one array)
|
|
177
196
|
autoload_method "self.concatenate", "carray/methods/composition"
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
# monkey patching is avoided -- only classes that explicitly `extend
|
|
16
16
|
# AutoloadMethodExtension` gain the method.
|
|
17
17
|
|
|
18
|
+
# @private
|
|
18
19
|
module AutoloadMethodExtension
|
|
19
20
|
# @!visibility private
|
|
20
21
|
def autoload_method (method, library)
|
|
@@ -31,7 +32,7 @@ module AutoloadMethodExtension
|
|
|
31
32
|
private
|
|
32
33
|
|
|
33
34
|
def autoload_define (target, name, library, original_spec)
|
|
34
|
-
target.
|
|
35
|
+
target.define_method(name) do |*args, **kwargs, &block|
|
|
35
36
|
begin
|
|
36
37
|
require library
|
|
37
38
|
rescue LoadError
|
data/lib/carray/axis_group.rb
CHANGED
|
@@ -401,8 +401,8 @@ class CAGroupIterator
|
|
|
401
401
|
end
|
|
402
402
|
|
|
403
403
|
# @overload cummax(axis: :group)
|
|
404
|
-
# Per-group inclusive running maximum, source-shaped, in the source
|
|
405
|
-
# (extrema do not grow magnitude, so the
|
|
404
|
+
# Per-group inclusive running maximum, source-shaped, in the source data type
|
|
405
|
+
# (extrema do not grow magnitude, so the data type is preserved). The first
|
|
406
406
|
# member of a group emits its own value. Without :group delegates to value
|
|
407
407
|
# cummax.
|
|
408
408
|
# @return [CArray]
|
|
@@ -411,7 +411,7 @@ class CAGroupIterator
|
|
|
411
411
|
end
|
|
412
412
|
|
|
413
413
|
# @overload cummin(axis: :group)
|
|
414
|
-
# Per-group inclusive running minimum, source-shaped, in the source
|
|
414
|
+
# Per-group inclusive running minimum, source-shaped, in the source data type.
|
|
415
415
|
# Without :group delegates to value cummin.
|
|
416
416
|
# @return [CArray]
|
|
417
417
|
def cummin (**kw)
|
|
@@ -612,7 +612,7 @@ class CAGroupIterator
|
|
|
612
612
|
unless value.respond_to?(op)
|
|
613
613
|
raise NotImplementedError, "axis_group scan: value has no ##{op}"
|
|
614
614
|
end
|
|
615
|
-
return value.
|
|
615
|
+
return value.public_send(op)
|
|
616
616
|
end
|
|
617
617
|
unless fused.empty?
|
|
618
618
|
raise ArgumentError,
|
|
@@ -630,7 +630,7 @@ class CAGroupIterator
|
|
|
630
630
|
# operation, and remains a follow-up.
|
|
631
631
|
def order_stat (op, args, kw)
|
|
632
632
|
has_group, fused = AxisGroup.parse_axis(kw[:axis])
|
|
633
|
-
return value.
|
|
633
|
+
return value.public_send(op, *args) unless has_group
|
|
634
634
|
unless fused.empty?
|
|
635
635
|
raise NotImplementedError,
|
|
636
636
|
"axis_group: folding a band into an order statistic (axis: [:group, k]) " \
|
|
@@ -651,13 +651,13 @@ class CAGroupIterator
|
|
|
651
651
|
ccat, kdims, gslots, bslots, gaxes = composite_layout
|
|
652
652
|
nout = (op == :quantile) ? 5 : 1
|
|
653
653
|
if bslots.empty?
|
|
654
|
-
res = value.group_by_category(ccat).
|
|
654
|
+
res = value.group_by_category(ccat).public_send(op, *args)
|
|
655
655
|
return nout == 1 ? res.reshape(*kdims) : res.map { |r| r.reshape(*kdims) }
|
|
656
656
|
end
|
|
657
657
|
out_shape = spec.slot_meta.map { |m| m[:kind] == :group ? m[:k] : m[:len] }
|
|
658
658
|
outs = Array.new(nout) { CArray.float64(*out_shape) }
|
|
659
659
|
each_band_block(ccat, gslots, bslots, gaxes) do |_vi, out_idx, _co, gi|
|
|
660
|
-
result = gi.
|
|
660
|
+
result = gi.public_send(op, *args)
|
|
661
661
|
if nout == 1
|
|
662
662
|
outs[0][*out_idx] = result.reshape(*kdims)
|
|
663
663
|
else
|
data/lib/carray/basics.rb
CHANGED
|
@@ -30,27 +30,26 @@ class CArray
|
|
|
30
30
|
# shift as other axes are inserted (e.g. `insert_axis(0, 1, 2)`
|
|
31
31
|
# puts one axis before each of the first three source axes).
|
|
32
32
|
#
|
|
33
|
-
# Each inserted axis takes one of
|
|
34
|
-
# `repeat` value: `1` (or `nil`) for a plain size-1 axis, an
|
|
35
|
-
# Integer `N > 1` for a read-only bound repeat view
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# giving one value per position.
|
|
33
|
+
# Each inserted axis takes one of two forms, chosen by its
|
|
34
|
+
# `repeat` value: `1` (or `nil`) for a plain size-1 axis, or an
|
|
35
|
+
# Integer `N > 1` for a read-only bound repeat view. `repeat` is
|
|
36
|
+
# either a single value applied to every inserted axis, or an
|
|
37
|
+
# Array giving one value per position.
|
|
39
38
|
#
|
|
40
|
-
# The everyday way to add an axis is the `:_`
|
|
41
|
-
#
|
|
42
|
-
#
|
|
39
|
+
# The everyday way to add an axis is the `:_` indexer when the
|
|
40
|
+
# shape is known at the call site; `insert_axis` is for library
|
|
41
|
+
# code that builds the axis list programmatically.
|
|
43
42
|
#
|
|
44
43
|
# @param positions [Array<Integer>] source-frame positions of the
|
|
45
44
|
# new axes.
|
|
46
|
-
# @param repeat [Integer,
|
|
47
|
-
#
|
|
45
|
+
# @param repeat [Integer, Array, nil] repeat spec applied to each
|
|
46
|
+
# inserted axis.
|
|
48
47
|
# @return [CArray] view with the new axes inserted.
|
|
49
48
|
# @example
|
|
50
49
|
# a = CArray.int32(3, 4).seq
|
|
51
50
|
# a.insert_axis(0) # shape (1, 3, 4)
|
|
52
51
|
# a.insert_axis(1, repeat: 5) # shape (3, 5, 4)
|
|
53
|
-
# a.insert_axis(0, 1, repeat: [
|
|
52
|
+
# a.insert_axis(0, 1, repeat: [1, 3]) # size-1 then bound
|
|
54
53
|
def insert_axis (*positions, repeat: nil)
|
|
55
54
|
flat = positions.flatten
|
|
56
55
|
if flat.empty?
|
|
@@ -81,57 +80,31 @@ class CArray
|
|
|
81
80
|
Array.new(flat.length, repeat)
|
|
82
81
|
end
|
|
83
82
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
when :*
|
|
91
|
-
# ok
|
|
92
|
-
else
|
|
93
|
-
raise ArgumentError,
|
|
94
|
-
"insert_axis: repeat must be a positive Integer or :*, got #{r.inspect}"
|
|
95
|
-
end
|
|
83
|
+
# Validate each value. A positive Integer only; nil is not a valid
|
|
84
|
+
# per-axis repeat.
|
|
85
|
+
reps.each do |r|
|
|
86
|
+
unless r.is_a?(Integer)
|
|
87
|
+
raise ArgumentError,
|
|
88
|
+
"insert_axis: repeat must be a positive Integer, got #{r.inspect}"
|
|
96
89
|
end
|
|
90
|
+
raise ArgumentError, "insert_axis: repeat count must be >= 1" if r < 1
|
|
91
|
+
end
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
order.each_with_index { |i, k| final[i] = gaps[i] + k }
|
|
106
|
-
|
|
107
|
-
unbound_args = order.select { |i| reps[i] == :* }
|
|
108
|
-
concrete_args = order.reject { |i| reps[i] == :* } # in output order
|
|
109
|
-
|
|
110
|
-
# Stage 1: insert the concrete (size-1 / bound) axes by their source
|
|
111
|
-
# gaps, then grow the bound ones with broadcast_to.
|
|
112
|
-
inter = self
|
|
113
|
-
unless concrete_args.empty?
|
|
114
|
-
inter = __insert_axis_size1__(*concrete_args.map { |i| gaps[i] })
|
|
115
|
-
if concrete_args.any? { |i| reps[i].is_a?(Integer) && reps[i] > 1 }
|
|
116
|
-
shp = inter.shape
|
|
117
|
-
concrete_args.each do |i|
|
|
118
|
-
r = reps[i]
|
|
119
|
-
next unless r.is_a?(Integer) && r > 1
|
|
120
|
-
# intermediate position = final position minus unbound axes before it
|
|
121
|
-
shp[final[i] - unbound_args.count { |u| final[u] < final[i] }] = r
|
|
122
|
-
end
|
|
123
|
-
inter = inter.broadcast_to(*shp)
|
|
124
|
-
end
|
|
125
|
-
end
|
|
93
|
+
# Final output layout: stable order by (gap, argument index) keeps
|
|
94
|
+
# same-gap axes in argument order; the k-th inserted axis lands at output
|
|
95
|
+
# position gap + k. This output position only drives broadcast_to; the
|
|
96
|
+
# insertion itself always goes through the source-frame primitive.
|
|
97
|
+
order = (0...flat.length).sort_by { |i| [gaps[i], i] }
|
|
98
|
+
final = {}
|
|
99
|
+
order.each_with_index { |i, k| final[i] = gaps[i] + k }
|
|
126
100
|
|
|
127
|
-
|
|
101
|
+
inter = __insert_axis_size1__(*order.map { |i| gaps[i] })
|
|
102
|
+
return inter unless order.any? { |i| reps[i] > 1 }
|
|
128
103
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
inter.unbound_repeat(*pattern)
|
|
134
|
-
end
|
|
104
|
+
shp = inter.shape
|
|
105
|
+
order.each { |i| shp[final[i]] = reps[i] if reps[i] > 1 }
|
|
106
|
+
inter.broadcast_to(*shp)
|
|
107
|
+
end
|
|
135
108
|
|
|
136
109
|
# @overload drop_axis
|
|
137
110
|
# Returns a view of `self` with every size-1 axis dropped.
|
|
@@ -246,6 +219,33 @@ class CArray
|
|
|
246
219
|
|
|
247
220
|
alias cmp <=>
|
|
248
221
|
|
|
222
|
+
# @!group Elementwise math
|
|
223
|
+
|
|
224
|
+
# Returns `[quotient, remainder]` element-wise.
|
|
225
|
+
#
|
|
226
|
+
# The quotient is floored toward -inf, so `q * other + r == self` holds
|
|
227
|
+
# for every sign combination -- the pair Ruby's `Integer#divmod` and
|
|
228
|
+
# `Float#divmod` return. For integers that is `self / other` unchanged;
|
|
229
|
+
# for floats `/` is true division, so the quotient is floored here.
|
|
230
|
+
#
|
|
231
|
+
# @param other [CArray, Numeric] divisor.
|
|
232
|
+
# @return [Array<CArray>] the floored quotient and the remainder.
|
|
233
|
+
# @raise [ArgumentError] for complex arrays, which have no ordering to
|
|
234
|
+
# floor toward.
|
|
235
|
+
# @example
|
|
236
|
+
# CA_INT32([-7]).divmod(3) # => [-3, 2]
|
|
237
|
+
# CA_DOUBLE([-7.0]).divmod(3.0) # => [-3.0, 2.0]
|
|
238
|
+
def divmod(other)
|
|
239
|
+
if complex?
|
|
240
|
+
raise ArgumentError, "divmod is not defined for complex arrays"
|
|
241
|
+
end
|
|
242
|
+
q = self / other
|
|
243
|
+
q = q.floor unless q.integer?
|
|
244
|
+
[q, self % other]
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# @!endgroup
|
|
248
|
+
|
|
249
249
|
# @overload clip(min, max = nil, fill_value = nil, lfill: nil, ufill: nil)
|
|
250
250
|
# Returns `self` with every element clamped to `[min, max]`.
|
|
251
251
|
#
|
data/lib/carray/bincount_nd.rb
CHANGED
|
@@ -68,16 +68,16 @@ class CArray
|
|
|
68
68
|
# use `CArray#histogram`.
|
|
69
69
|
class BincountND
|
|
70
70
|
|
|
71
|
-
# @overload initialize(lengths:, fiber_shape: [],
|
|
71
|
+
# @overload initialize(lengths:, fiber_shape: [], weights_data_type: nil)
|
|
72
72
|
# Allocates a new N-D discrete bincount accumulator.
|
|
73
73
|
# @param lengths [Array<Integer>] per-dimension label ranges;
|
|
74
74
|
# each must be `>= 1`.
|
|
75
75
|
# @param fiber_shape [Array<Integer>] shape of the leading
|
|
76
76
|
# axes.
|
|
77
|
-
# @param
|
|
77
|
+
# @param weights_data_type [Symbol, nil] `data_type` for weighted
|
|
78
78
|
# accumulators; `nil` for pure counts (int64).
|
|
79
79
|
# @return [BincountND]
|
|
80
|
-
def initialize (lengths:, fiber_shape: [],
|
|
80
|
+
def initialize (lengths:, fiber_shape: [], weights_data_type: nil)
|
|
81
81
|
@lengths = lengths.map(&:to_i)
|
|
82
82
|
raise ArgumentError, "lengths must be a non-empty list" if @lengths.empty?
|
|
83
83
|
@lengths.each_with_index do |l, k|
|
|
@@ -85,11 +85,11 @@ class CArray
|
|
|
85
85
|
end
|
|
86
86
|
@m = @lengths.size
|
|
87
87
|
@fiber_shape = fiber_shape.map(&:to_i).freeze
|
|
88
|
-
@weighted = !
|
|
89
|
-
@
|
|
88
|
+
@weighted = !weights_data_type.nil?
|
|
89
|
+
@counts_data_type = @weighted ? weights_data_type : :int64
|
|
90
90
|
ext_dims = @lengths.map { |l| l + 1 } # +1: upper overflow cell
|
|
91
91
|
ext_shape = @fiber_shape + ext_dims
|
|
92
|
-
@full_counts = CArray.public_send(@
|
|
92
|
+
@full_counts = CArray.public_send(@counts_data_type, *ext_shape).fill(0)
|
|
93
93
|
@sample_axis = nil
|
|
94
94
|
@channel_axis = nil
|
|
95
95
|
end
|
|
@@ -209,7 +209,7 @@ class CArray
|
|
|
209
209
|
|
|
210
210
|
if weights
|
|
211
211
|
raise ArgumentError, "weights given but accumulator is unweighted" unless @weighted
|
|
212
|
-
weights = CArray.wrap_readonly(weights, @
|
|
212
|
+
weights = CArray.wrap_readonly(weights, @counts_data_type)
|
|
213
213
|
expected_w_shape = chunk.shape.dup
|
|
214
214
|
expected_w_shape.delete_at(channel_ax)
|
|
215
215
|
unless weights.shape == expected_w_shape
|
|
@@ -297,7 +297,7 @@ class CArray
|
|
|
297
297
|
result = self.class.send(:new,
|
|
298
298
|
lengths: @lengths,
|
|
299
299
|
fiber_shape: @fiber_shape,
|
|
300
|
-
|
|
300
|
+
weights_data_type: @weighted ? @counts_data_type : nil)
|
|
301
301
|
rf = result.instance_variable_get(:@full_counts)
|
|
302
302
|
rf[] = @full_counts + other.full_counts
|
|
303
303
|
result.instance_variable_set(:@sample_axis, @sample_axis)
|
|
@@ -344,14 +344,14 @@ class CArray
|
|
|
344
344
|
[sample_ax, channel_ax].sort.reverse.each { |p| fiber_shape.delete_at(p) }
|
|
345
345
|
|
|
346
346
|
# Weighted counts are float64-only (the FLAT bincount coerces weights to the
|
|
347
|
-
# counts
|
|
348
|
-
#
|
|
349
|
-
|
|
347
|
+
# counts data type and the FIBER kernel requires float64 weights/counts), so
|
|
348
|
+
# the type is fixed here rather than derived from the weights' own type.
|
|
349
|
+
weights_data_type = (:float64 if weights)
|
|
350
350
|
|
|
351
351
|
h = BincountND.send(:new,
|
|
352
352
|
lengths: lengths,
|
|
353
353
|
fiber_shape: fiber_shape,
|
|
354
|
-
|
|
354
|
+
weights_data_type: weights_data_type)
|
|
355
355
|
h.add(self, axis: axis, weights: weights)
|
|
356
356
|
h
|
|
357
357
|
end
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# is a `block_view` (a CAStride: zero-copy over the source via compose-fold),
|
|
20
20
|
# reduced over the trailing tile axes by a core reduction and scattered into the
|
|
21
21
|
# ceil-shaped tile grid. One core reduction per region; the named reductions
|
|
22
|
-
# delegate straight to the core, so their
|
|
22
|
+
# delegate straight to the core, so their data type / mask / empty (ERI) / epsilon
|
|
23
23
|
# contracts are the core's, unchanged. Because tiles do not overlap, no
|
|
24
24
|
# padded entity is built (unlike CAWindowIterator) and the interior stays on
|
|
25
25
|
# the source buffer.
|
|
@@ -165,7 +165,7 @@ class CABlockIterator < CAIterator
|
|
|
165
165
|
# quantile) which are assembled into that many grids. Boundary regions use a
|
|
166
166
|
# smaller (unmasked, ragged) block_view whose present cell count is naturally
|
|
167
167
|
# below a full tile, so `min_count:` marks them UNDEF with no masking. The
|
|
168
|
-
# output
|
|
168
|
+
# output data type is seeded from the first region (all regions share it).
|
|
169
169
|
def assemble
|
|
170
170
|
outs = nil
|
|
171
171
|
each_region do |strip_ranges, tiles, out_ranges|
|
|
@@ -184,7 +184,7 @@ class CABlockIterator < CAIterator
|
|
|
184
184
|
|
|
185
185
|
# Fold every region with a single core reduction `op` (the tier-1 shape).
|
|
186
186
|
def fold (op, **kw)
|
|
187
|
-
assemble { |view, _tiles| view.
|
|
187
|
+
assemble { |view, _tiles| view.public_send(op, axis: @tile_axes, **kw) }
|
|
188
188
|
end
|
|
189
189
|
|
|
190
190
|
public
|
|
@@ -194,14 +194,20 @@ class CABlockIterator < CAIterator
|
|
|
194
194
|
# A per-tile fold to one value over the trailing tile axes is exactly a core
|
|
195
195
|
# per-axis reduction over those axes, so every reduction delegates to
|
|
196
196
|
# `block_view.<op>(axis: tile_axes, ...)` per region. This inherits the core
|
|
197
|
-
#
|
|
197
|
+
# data type, mask, empty / all-masked (identity vs UNDEF) and epsilon-close
|
|
198
198
|
# contracts unchanged. `min_count:` / `fill_value:` pass straight to the core.
|
|
199
199
|
|
|
200
200
|
# @overload sum(min_count: nil, fill_value: nil)
|
|
201
201
|
# Per-tile sum. @return [CArray] tile-grid shaped
|
|
202
|
+
# @overload accumulate(min_count: nil, fill_value: nil)
|
|
203
|
+
# Per-tile sum kept in the source's own data type, wrapping at its width,
|
|
204
|
+
# as the core `accumulate` does -- `sum` answers in the type the core
|
|
205
|
+
# promotes to (float64 for integers), so a tile count over `uint8` cells
|
|
206
|
+
# stays one byte wide instead of eight.
|
|
207
|
+
# @return [CArray] tile-grid shaped
|
|
202
208
|
# The rest are analogous: prod / mean / min / max, sample and population
|
|
203
209
|
# variance / stddev, all / any.
|
|
204
|
-
[:sum, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
|
|
210
|
+
[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
|
|
205
211
|
:variancep, :stddevp].each do |op|
|
|
206
212
|
define_method(op) do |min_count: nil, fill_value: nil|
|
|
207
213
|
kw = {}
|
|
@@ -281,7 +287,11 @@ class CABlockIterator < CAIterator
|
|
|
281
287
|
# Per-tile position of the maximum (tile-local flat index).
|
|
282
288
|
# @return [CArray]
|
|
283
289
|
[:min_index, :max_index].each do |op|
|
|
284
|
-
|
|
290
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
291
|
+
def #{op}
|
|
292
|
+
assemble { |view, _| view.#{op}(axis: @tile_axes) }
|
|
293
|
+
end
|
|
294
|
+
RUBY
|
|
285
295
|
end
|
|
286
296
|
|
|
287
297
|
# @overload min_addr
|
|
@@ -309,7 +319,7 @@ class CABlockIterator < CAIterator
|
|
|
309
319
|
each_region do |strip_ranges, tiles, out_ranges|
|
|
310
320
|
vview = @source[*strip_ranges].block_view(*tiles)
|
|
311
321
|
sview = saddr[*strip_ranges].block_view(*tiles)
|
|
312
|
-
mi = vview.
|
|
322
|
+
mi = vview.public_send(idx_op, axis: @tile_axes) # tile-local flat index
|
|
313
323
|
grid = (0...@sndim).map { |i| vview.shape[i] }
|
|
314
324
|
cells = tiles.inject(1) { |p, t| p * t }
|
|
315
325
|
safe = mi.copy
|
|
@@ -380,7 +390,7 @@ class CABlockIterator < CAIterator
|
|
|
380
390
|
wt = tiles == @sizes ? weights : weights[*tiles.map { |t| 0...t }]
|
|
381
391
|
wshape = ([1] * @sndim) + tiles
|
|
382
392
|
# Pass the weights through unchanged, like the window iterator: core wsum /
|
|
383
|
-
# wmean own weight/
|
|
393
|
+
# wmean own weight / type coercion, so do not pre-coerce the weights here.
|
|
384
394
|
wfull = wt.reshape(*wshape).broadcast_to(*view.shape)
|
|
385
395
|
yield view, wfull
|
|
386
396
|
end
|
|
@@ -516,8 +526,8 @@ class CABlockIterator < CAIterator
|
|
|
516
526
|
# reusing the same padded (masked-margin) entity as the iterate escape
|
|
517
527
|
# hatches, so this is the slow path (a per-tile materialize), and the
|
|
518
528
|
# out-of-bounds cells of a partial edge tile are dropped from the result.
|
|
519
|
-
# cumsum / cumprod -> float64, cummax / cummin preserve the value
|
|
520
|
-
# cumcount -> int64 running count of present cells; the output
|
|
529
|
+
# cumsum / cumprod -> float64, cummax / cummin preserve the value data type,
|
|
530
|
+
# cumcount -> int64 running count of present cells; the output data type is seeded
|
|
521
531
|
# from the first tile's scan.
|
|
522
532
|
|
|
523
533
|
# @overload cumsum
|
|
@@ -527,10 +537,10 @@ class CABlockIterator < CAIterator
|
|
|
527
537
|
# Per-tile inclusive running product (float64), source-shaped.
|
|
528
538
|
# @return [CArray]
|
|
529
539
|
# @overload cummax
|
|
530
|
-
# Per-tile inclusive running maximum (value
|
|
540
|
+
# Per-tile inclusive running maximum (value data type), source-shaped.
|
|
531
541
|
# @return [CArray]
|
|
532
542
|
# @overload cummin
|
|
533
|
-
# Per-tile inclusive running minimum (value
|
|
543
|
+
# Per-tile inclusive running minimum (value data type), source-shaped.
|
|
534
544
|
# @return [CArray]
|
|
535
545
|
# @overload cumcount
|
|
536
546
|
# Per-tile running count of present cells (int64), source-shaped.
|
|
@@ -542,7 +552,7 @@ class CABlockIterator < CAIterator
|
|
|
542
552
|
private
|
|
543
553
|
|
|
544
554
|
# Drive a within-tile segment scan: scan each tile flattened row-major and
|
|
545
|
-
# scatter back to a source-shaped result. The output
|
|
555
|
+
# scatter back to a source-shaped result. The output data type is taken from the
|
|
546
556
|
# first tile's scan (all tiles share it); the padded margin is UNDEF and the
|
|
547
557
|
# OOB cells of a partial edge tile are cropped from the result.
|
|
548
558
|
def block_scan (op)
|
|
@@ -552,7 +562,7 @@ class CABlockIterator < CAIterator
|
|
|
552
562
|
pgv = nil
|
|
553
563
|
CArray.each_index(*@shape) do |*g|
|
|
554
564
|
tile = tgv[*g, *nils]
|
|
555
|
-
scanned = tile.copy.reshape(tile.elements).
|
|
565
|
+
scanned = tile.copy.reshape(tile.elements).public_send(op).reshape(*@sizes)
|
|
556
566
|
unless pout
|
|
557
567
|
pout = CArray.new(scanned.data_type, padded_source.shape)
|
|
558
568
|
pout[] = UNDEF
|
data/lib/carray/categorical.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
#
|
|
3
3
|
# carray/categorical.rb
|
|
4
4
|
#
|
|
5
|
-
# CACategorical — a categorical
|
|
5
|
+
# CACategorical — a categorical data type: dense integer codes (the storage) plus
|
|
6
6
|
# a label vocabulary. Structurally the same as a pandas Categorical or an
|
|
7
7
|
# Arrow dictionary: each element is an index into a small set of categories.
|
|
8
8
|
#
|
|
@@ -66,7 +66,7 @@ class CACategorical < CAObject
|
|
|
66
66
|
}.freeze
|
|
67
67
|
|
|
68
68
|
# The exclusion sentinel per codes data_type: the all-ones bit pattern, read
|
|
69
|
-
# as type-max for an unsigned
|
|
69
|
+
# as type-max for an unsigned data type and as -1 for a signed one. Either way it
|
|
70
70
|
# is out of every valid [0, k) range and byte-identical to a pandas / Arrow
|
|
71
71
|
# missing code.
|
|
72
72
|
SENTINEL = {
|
|
@@ -521,7 +521,7 @@ class CArray
|
|
|
521
521
|
# Integer / Float keys stay distinct (eql?, so 1 and 1.0 are separate
|
|
522
522
|
# categories). The discovery path below is reserved for sort_labels (which
|
|
523
523
|
# reorders the vocabulary, desyncing the appearance-order codes), an explicit
|
|
524
|
-
# labels list, and the
|
|
524
|
+
# labels list, and the data types the factorize kernel does not take (complex).
|
|
525
525
|
if labels.nil? && !sort_labels && (integer? || float? || object? || fixlen? || boolean?)
|
|
526
526
|
codes, levels = __factorize_appearance__
|
|
527
527
|
return CACategorical.from_codes(codes, levels.to_a)
|
|
@@ -541,7 +541,7 @@ class CArray
|
|
|
541
541
|
end
|
|
542
542
|
end
|
|
543
543
|
|
|
544
|
-
# Choose a narrow unsigned code
|
|
544
|
+
# Choose a narrow unsigned code type, reserving its top value as the
|
|
545
545
|
# exclusion sentinel so it never collides with a real code 0..k-1.
|
|
546
546
|
k = labels_arr.size
|
|
547
547
|
code_type, sentinel =
|