carray 3.0.0 → 3.0.2
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 +845 -3
- data/{NEWS.md → CHANGELOG.v1.md} +1 -1
- data/README.md +33 -33
- data/carray.gemspec +12 -8
- data/ext/ca_axis_dispatch.c +33 -4
- data/ext/ca_axis_group.c +256 -114
- data/ext/ca_binop_dispatch.c +5 -6
- data/ext/ca_binop_dispatch.h +0 -7
- data/ext/ca_categorical_iterator.c +121 -67
- data/ext/ca_for_each_element.h +6 -8
- data/ext/ca_group_iter.c +1 -1
- data/ext/ca_kernel_iterator.c +386 -74
- data/ext/ca_kernel_iterator.h +145 -45
- data/ext/ca_obj_array.c +62 -20
- data/ext/ca_obj_bincmp.c +57 -27
- data/ext/ca_obj_binop.c +86 -30
- data/ext/ca_obj_block.c +4 -4
- data/ext/ca_obj_const_string.c +88 -27
- data/ext/ca_obj_face.c +104 -0
- data/ext/ca_obj_face.h +48 -1
- data/ext/ca_obj_fake.c +11 -0
- data/ext/ca_obj_fixlen_string.c +21 -6
- data/ext/ca_obj_grid.c +11 -3
- data/ext/ca_obj_meld.c +125 -22
- data/ext/ca_obj_moncmp.c +33 -14
- data/ext/ca_obj_monop.c +19 -7
- data/ext/ca_obj_object.c +107 -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.c +49 -34
- data/ext/ca_obj_select_axis.c +16 -6
- data/ext/ca_obj_shift.c +3 -3
- data/ext/ca_obj_stack.c +3 -8
- data/ext/ca_obj_stride.c +178 -24
- data/ext/ca_obj_string.c +11 -5
- 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 +94 -19
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +1 -5
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sort_kernels.h +5 -5
- data/ext/ca_sweep_engine.c +362 -155
- data/ext/ca_sweep_engine.h +39 -9
- data/ext/ca_transform_common.c +7 -0
- data/ext/carray.h +148 -132
- data/ext/carray_access.c +83 -49
- data/ext/carray_address_basis.c +590 -0
- data/ext/carray_bincount.c +8 -8
- data/ext/carray_broadcast.c +102 -9
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +2010 -335
- data/ext/carray_call_cfunc.h +153 -0
- data/ext/carray_cast.c +184 -72
- data/ext/carray_conversion.c +22 -22
- data/ext/carray_copy.c +67 -32
- data/ext/carray_core.c +140 -13
- data/ext/carray_count.c +10 -11
- data/ext/carray_element.c +7 -7
- data/ext/carray_factorize.c +72 -51
- data/ext/carray_hold.c +1 -1
- data/ext/carray_index_classifier.c +7 -21
- data/ext/carray_internal.h +36 -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 +256 -87
- data/ext/carray_kernels_reduce_boolean.c +1 -0
- data/ext/carray_kernels_reduce_cumulative.c +324 -54
- data/ext/carray_kernels_reduce_extreme.c +605 -58
- data/ext/carray_kernels_reduce_variance.c +1 -0
- data/ext/carray_kernels_scan.c +5 -4
- data/ext/carray_kernels_search.c +187 -106
- 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_loop.c +7 -1
- data/ext/carray_mask.c +29 -13
- data/ext/carray_median_percentile.c +56 -1
- data/ext/carray_memory_view.c +46 -21
- data/ext/carray_operator.c +50 -53
- data/ext/carray_order.c +1 -1
- data/ext/carray_partition.c +4 -4
- data/ext/carray_random.c +391 -47
- data/ext/carray_scatter.c +1 -1
- data/ext/carray_slab.c +13 -0
- data/ext/carray_sort.c +22 -24
- 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 +323 -145
- data/ext/mkkernel.rb +651 -129
- data/ext/ruby_carray.c +28 -17
- data/ext/version.h +4 -4
- data/lib/carray/autoload_carray.rb +24 -3
- data/lib/carray/autoload_method_extension.rb +14 -1
- data/lib/carray/axis_group.rb +84 -7
- data/lib/carray/basics.rb +63 -59
- data/lib/carray/bincount_nd.rb +12 -12
- data/lib/carray/block_iterator.rb +111 -25
- data/lib/carray/categorical.rb +154 -37
- data/lib/carray/categorical_iterator.rb +300 -151
- data/lib/carray/conditional.rb +14 -14
- data/lib/carray/const_string.rb +131 -27
- data/lib/carray/construct.rb +61 -0
- data/lib/carray/core_extensions.rb +62 -5
- data/lib/carray/data_type_extension.rb +21 -1
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/concat.rb +3 -3
- data/lib/carray/frame/convert.rb +1 -1
- data/lib/carray/frame/csv_parser.rb +11 -4
- data/lib/carray/frame/frame.rb +85 -21
- data/lib/carray/frame/group.rb +36 -3
- data/lib/carray/frame/io.rb +224 -15
- data/lib/carray/frame/records.rb +18 -4
- data/lib/carray/frame/sort.rb +1 -1
- data/lib/carray/frame/verbs.rb +32 -12
- 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 +43 -16
- data/lib/carray/iterator.rb +147 -3
- data/lib/carray/lazy.rb +88 -73
- data/lib/carray/mask_gap_fill.rb +3 -1
- 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/discovery_along.rb +74 -0
- data/lib/carray/methods/factorize.rb +50 -0
- data/lib/carray/methods/is_in.rb +25 -14
- data/lib/carray/methods/locate_addr.rb +81 -3
- data/lib/carray/methods/mask_duplicates.rb +36 -2
- data/lib/carray/methods/meshgrid.rb +4 -5
- data/lib/carray/methods/mode.rb +2 -2
- data/lib/carray/methods/nunique.rb +23 -2
- data/lib/carray/methods/repeat.rb +110 -0
- data/lib/carray/methods/snap.rb +7 -2
- data/lib/carray/methods/unique.rb +44 -4
- data/lib/carray/methods/value_counts.rb +2 -2
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/runtime.rb +0 -19
- data/lib/carray/slab_iterator.rb +77 -19
- data/lib/carray/string_operation_extension.rb +10 -6
- data/lib/carray/time.rb +906 -491
- data/lib/carray/window_iterator.rb +421 -27
- data/lib/carray.rb +7 -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 +68 -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 +57 -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 +34 -0
- data/yard-stubs/carray_count.rb +84 -0
- data/yard-stubs/carray_element.rb +108 -0
- data/yard-stubs/carray_generate.rb +66 -0
- data/yard-stubs/carray_lazy.rb +228 -0
- data/yard-stubs/carray_loop.rb +140 -0
- data/yard-stubs/carray_mask.rb +259 -0
- data/yard-stubs/carray_math.rb +1615 -0
- data/yard-stubs/carray_mathfunc.rb +45 -0
- data/yard-stubs/carray_median_percentile.rb +103 -0
- data/yard-stubs/carray_memory_view.rb +163 -0
- data/yard-stubs/carray_order.rb +252 -0
- data/yard-stubs/carray_random.rb +89 -0
- data/yard-stubs/carray_scatter.rb +106 -0
- data/yard-stubs/carray_slab.rb +59 -0
- data/yard-stubs/carray_sort.rb +165 -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 +67 -10
- data/ext/ca_obj_unbound_repeat.c +0 -496
data/ext/ca_axis_group.c
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
reduce dispatch, GroupLabels) is wired in ca_group_iter.c (the `[]` gate and
|
|
9
9
|
the iterator) + `axis_group` (lib/carray/axis_group.rb). Both kernels are
|
|
10
10
|
written in the general form: several group axes + rank-N categorical (an N-D
|
|
11
|
-
codes map) via a per-slab-element composite code, native
|
|
11
|
+
codes map) via a per-slab-element composite code, native data type dispatch,
|
|
12
12
|
mask support. They take pre-built code bundles, so they are independent of
|
|
13
13
|
how the classifier is constructed.
|
|
14
14
|
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
enum {
|
|
41
41
|
GR_SUM = 0, GR_PROD, GR_MEAN, GR_MIN, GR_MAX,
|
|
42
42
|
GR_VARIANCE, GR_STDDEV, GR_COUNT, GR_ALL, GR_ANY,
|
|
43
|
-
GR_VARIANCEP, GR_STDDEVP, GR_MINADDR, GR_MAXADDR
|
|
43
|
+
GR_VARIANCEP, GR_STDDEVP, GR_MINADDR, GR_MAXADDR,
|
|
44
|
+
GR_ACCUM
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
static int
|
|
@@ -48,6 +49,7 @@ group_op_code (VALUE vop)
|
|
|
48
49
|
{
|
|
49
50
|
ID id = SYM2ID(vop);
|
|
50
51
|
if ( id == rb_intern("sum") ) return GR_SUM;
|
|
52
|
+
else if ( id == rb_intern("accumulate") ) return GR_ACCUM;
|
|
51
53
|
else if ( id == rb_intern("prod") ) return GR_PROD;
|
|
52
54
|
else if ( id == rb_intern("mean") ) return GR_MEAN;
|
|
53
55
|
else if ( id == rb_intern("min") ) return GR_MIN;
|
|
@@ -72,14 +74,14 @@ group_op_code (VALUE vop)
|
|
|
72
74
|
group code, applies the mask, and folds the value into whichever
|
|
73
75
|
accumulator buffers are non-NULL. Accumulation is always in `double` (or
|
|
74
76
|
counts in ca_size_t), so the op-finalisation below is type-agnostic — only
|
|
75
|
-
the LOAD is monomorphised per
|
|
77
|
+
the LOAD is monomorphised per data type, keeping the inner loop autovectorisable
|
|
76
78
|
while avoiding a forced float64 materialise of the (large) source. */
|
|
77
79
|
|
|
78
80
|
/* GROUP_WALK(T, ACCUM): one CA_FOR_EACH_SLAB pass. For each slab element it
|
|
79
81
|
computes the composite group code, applies the mask, then runs ACCUM with
|
|
80
82
|
`v` (the element, widened to double) and `o` (the output flat index =
|
|
81
83
|
code * band + band_flat) in scope. ACCUM is the only per-op-varying part,
|
|
82
|
-
so the
|
|
84
|
+
so the data type is monomorphised once per type while sum / mean / variance /
|
|
83
85
|
... reuse the same walk. */
|
|
84
86
|
/* Sentinel o-code for a slab element whose composite code is out of range
|
|
85
87
|
(excluded categorical), stored in the precomputed plan below. */
|
|
@@ -108,16 +110,20 @@ group_op_code (VALUE vop)
|
|
|
108
110
|
ca_size_t *gw_ocode = NULL, *gw_doff = NULL, *gw_moff = NULL, \
|
|
109
111
|
*gw_gaddr = NULL; \
|
|
110
112
|
int gw_ready = 0; \
|
|
113
|
+
/* freed by the unwind too: the walk can raise between here and the \
|
|
114
|
+
releases below (a gather that cannot convert a cell, and in the object \
|
|
115
|
+
lane a callback into Ruby). */ \
|
|
116
|
+
volatile VALUE gw_h1 = 0, gw_h2 = 0, gw_h3 = 0, gw_h4 = 0; \
|
|
111
117
|
int gw_need_addr = ( op == GR_MINADDR || op == GR_MAXADDR ); \
|
|
112
118
|
CA_FOR_EACH_SLAB(st, ca, axes, (int8_t) ngroup, CA_KERNEL_READ, p, m) { \
|
|
113
119
|
int8_t sndim = st.slab_ndim; \
|
|
114
120
|
ca_size_t SE = st.slab_elements; \
|
|
115
121
|
if ( SE > 0 ) { \
|
|
116
122
|
if ( ! gw_ready ) { \
|
|
117
|
-
gw_ocode =
|
|
118
|
-
gw_doff =
|
|
119
|
-
gw_moff =
|
|
120
|
-
gw_gaddr =
|
|
123
|
+
gw_ocode = ALLOCV_N(ca_size_t, gw_h1, SE); \
|
|
124
|
+
gw_doff = ALLOCV_N(ca_size_t, gw_h2, SE); \
|
|
125
|
+
gw_moff = ALLOCV_N(ca_size_t, gw_h3, SE); \
|
|
126
|
+
gw_gaddr = ALLOCV_N(ca_size_t, gw_h4, SE); \
|
|
121
127
|
ca_size_t sidx[CA_RANK_MAX]; \
|
|
122
128
|
for ( int8_t k = 0; k < sndim; k++ ) sidx[k] = 0; \
|
|
123
129
|
for ( ca_size_t e = 0; e < SE; e++ ) { \
|
|
@@ -156,22 +162,96 @@ group_op_code (VALUE vop)
|
|
|
156
162
|
ca_size_t oc = gw_ocode[e]; \
|
|
157
163
|
if ( oc == GW_SKIP ) continue; \
|
|
158
164
|
if ( m && m[ gw_moff[e] ] ) continue; \
|
|
159
|
-
|
|
165
|
+
T rv = *(T *)(p + gw_doff[e]); \
|
|
166
|
+
double v = (double) rv; \
|
|
160
167
|
ca_size_t o = oc + b; \
|
|
161
168
|
ca_size_t gaddr = gw_gaddr[e]; \
|
|
162
169
|
ACCUM; \
|
|
163
|
-
(void) v; (void) o; (void) gaddr;
|
|
170
|
+
(void) rv; (void) v; (void) o; (void) gaddr; \
|
|
164
171
|
} \
|
|
165
172
|
} \
|
|
166
173
|
b++; \
|
|
167
174
|
} \
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if ( gw_moff ) xfree(gw_moff); \
|
|
171
|
-
if ( gw_gaddr ) xfree(gw_gaddr); \
|
|
175
|
+
ALLOCV_END(gw_h1); ALLOCV_END(gw_h2); \
|
|
176
|
+
ALLOCV_END(gw_h3); ALLOCV_END(gw_h4); \
|
|
172
177
|
} while (0)
|
|
173
178
|
|
|
174
|
-
/*
|
|
179
|
+
/* GROUP_DISPATCH_T(ACCUM_T): the same walk, for an accumulator that needs to
|
|
180
|
+
name the source type -- one that keeps its answer in that type instead of
|
|
181
|
+
widening to double. ACCUM_T is invoked as ACCUM_T(T). */
|
|
182
|
+
#define GROUP_DISPATCH_T(ACCUM_T) \
|
|
183
|
+
switch ( ca->data_type ) { \
|
|
184
|
+
case CA_BOOLEAN: GROUP_WALK(boolean8_t, ACCUM_T(boolean8_t)); break; \
|
|
185
|
+
case CA_INT8: GROUP_WALK(int8_t, ACCUM_T(int8_t)); break; \
|
|
186
|
+
case CA_UINT8: GROUP_WALK(uint8_t, ACCUM_T(uint8_t)); break; \
|
|
187
|
+
case CA_INT16: GROUP_WALK(int16_t, ACCUM_T(int16_t)); break; \
|
|
188
|
+
case CA_UINT16: GROUP_WALK(uint16_t, ACCUM_T(uint16_t)); break; \
|
|
189
|
+
case CA_INT32: GROUP_WALK(int32_t, ACCUM_T(int32_t)); break; \
|
|
190
|
+
case CA_UINT32: GROUP_WALK(uint32_t, ACCUM_T(uint32_t)); break; \
|
|
191
|
+
case CA_INT64: GROUP_WALK(int64_t, ACCUM_T(int64_t)); break; \
|
|
192
|
+
case CA_UINT64: GROUP_WALK(uint64_t, ACCUM_T(uint64_t)); break; \
|
|
193
|
+
case CA_FLOAT32: GROUP_WALK(float, ACCUM_T(float)); break; \
|
|
194
|
+
case CA_FLOAT64: GROUP_WALK(double, ACCUM_T(double)); break; \
|
|
195
|
+
default: break; \
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* An extremum keeps the source data type: its magnitude never grows, so
|
|
199
|
+
widening to double buys nothing and costs exactness -- an int64 past 2^53
|
|
200
|
+
came back rounded, and a genuinely larger value could lose the comparison
|
|
201
|
+
to a smaller one that rounded to the same double. The scan siblings
|
|
202
|
+
(GROUP_SCAN_EXTREMUM_WALK) already held a native accumulator; these are the
|
|
203
|
+
reduce twins.
|
|
204
|
+
|
|
205
|
+
A NaN loses every contest, as it does in CArray's own min / max: it is held
|
|
206
|
+
only while nothing else has landed, the first number displaces it, and a
|
|
207
|
+
group of nothing but NaN answers NaN (for a position, UNDEF). `v` is the
|
|
208
|
+
widened load, so testing it for NaN is the same question for every source
|
|
209
|
+
type and costs an integer body nothing. seen_num[] says whether a number
|
|
210
|
+
has landed; cnt[] still says whether anything has. */
|
|
211
|
+
#define GMINMAX(T, A, CMP) \
|
|
212
|
+
do { \
|
|
213
|
+
A *acc = (A *) co->ptr; \
|
|
214
|
+
A av = (A) rv; \
|
|
215
|
+
if ( v == v ) { \
|
|
216
|
+
if ( ! seen_num[o] ) { acc[o] = av; seen_num[o] = 1; } \
|
|
217
|
+
else if ( av CMP acc[o] ) acc[o] = av; \
|
|
218
|
+
} else if ( cnt[o] == 0 ) acc[o] = av; \
|
|
219
|
+
cnt[o] += 1; \
|
|
220
|
+
} while (0)
|
|
221
|
+
|
|
222
|
+
/* Boolean answers as its 0/1 numeric storage, which is what CArray#min / #max
|
|
223
|
+
do with a boolean array -- the boolean-returning twins are all / any. Every
|
|
224
|
+
other type answers in its own. */
|
|
225
|
+
#define GROUP_DISPATCH_EXTREMUM(CMP) \
|
|
226
|
+
switch ( ca->data_type ) { \
|
|
227
|
+
case CA_BOOLEAN: GROUP_WALK(boolean8_t, GMINMAX(boolean8_t, uint64_t, CMP)); break; \
|
|
228
|
+
case CA_INT8: GROUP_WALK(int8_t, GMINMAX(int8_t, int8_t, CMP)); break; \
|
|
229
|
+
case CA_UINT8: GROUP_WALK(uint8_t, GMINMAX(uint8_t, uint8_t, CMP)); break; \
|
|
230
|
+
case CA_INT16: GROUP_WALK(int16_t, GMINMAX(int16_t, int16_t, CMP)); break; \
|
|
231
|
+
case CA_UINT16: GROUP_WALK(uint16_t, GMINMAX(uint16_t, uint16_t, CMP)); break; \
|
|
232
|
+
case CA_INT32: GROUP_WALK(int32_t, GMINMAX(int32_t, int32_t, CMP)); break; \
|
|
233
|
+
case CA_UINT32: GROUP_WALK(uint32_t, GMINMAX(uint32_t, uint32_t, CMP)); break; \
|
|
234
|
+
case CA_INT64: GROUP_WALK(int64_t, GMINMAX(int64_t, int64_t, CMP)); break; \
|
|
235
|
+
case CA_UINT64: GROUP_WALK(uint64_t, GMINMAX(uint64_t, uint64_t, CMP)); break; \
|
|
236
|
+
case CA_FLOAT32: GROUP_WALK(float, GMINMAX(float, float, CMP)); break; \
|
|
237
|
+
case CA_FLOAT64: GROUP_WALK(double, GMINMAX(double, double, CMP)); break; \
|
|
238
|
+
default: break; \
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
#define GMINMAXADDR(T, CMP, ADDR) \
|
|
242
|
+
do { \
|
|
243
|
+
T *acc = (T *) xbuf; \
|
|
244
|
+
if ( v == v && ( ! seen_num[o] || rv CMP acc[o] ) ) { \
|
|
245
|
+
acc[o] = rv; \
|
|
246
|
+
ADDR[o] = (int64_t) ( band_addr[b] + gaddr ); \
|
|
247
|
+
seen_num[o] = 1; \
|
|
248
|
+
} \
|
|
249
|
+
cnt[o] += 1; \
|
|
250
|
+
} while (0)
|
|
251
|
+
#define GMINADDR(T) GMINMAXADDR(T, <, mnaddr)
|
|
252
|
+
#define GMAXADDR(T) GMINMAXADDR(T, >, mxaddr)
|
|
253
|
+
|
|
254
|
+
/* Run one walk over every supported native data type. Dispatched on the source
|
|
175
255
|
data_type so the inner loop stays monomorphic (no forced float64 cast). */
|
|
176
256
|
#define GROUP_DISPATCH(ACCUM) \
|
|
177
257
|
switch ( ca->data_type ) { \
|
|
@@ -189,6 +269,19 @@ group_op_code (VALUE vop)
|
|
|
189
269
|
default: break; \
|
|
190
270
|
}
|
|
191
271
|
|
|
272
|
+
/* `accumulate` is the one op that folds in the SOURCE's own type instead of in
|
|
273
|
+
double, so it wraps at that width exactly as CArray#accumulate does. It
|
|
274
|
+
folds straight into the (zeroed) output — whose data type is the source's —
|
|
275
|
+
so an empty group already holds the additive identity 0 and needs no mask.
|
|
276
|
+
The type cannot travel inside a GROUP_WALK ACCUM argument (an argument's own
|
|
277
|
+
tokens are not substituted for the macro's parameters), so the walk below
|
|
278
|
+
names its type twice. */
|
|
279
|
+
#define GACC_ADD(T) ( ((T *) co->ptr)[o] += *(T *)(p + gw_doff[e]) )
|
|
280
|
+
/* A boolean accumulate is XOR parity, matching the core: the result stays
|
|
281
|
+
boolean, so a second `true` has nowhere to carry into. */
|
|
282
|
+
#define GACC_XOR \
|
|
283
|
+
( ((boolean8_t *) co->ptr)[o] ^= (*(boolean8_t *)(p + gw_doff[e]) ? 1 : 0) )
|
|
284
|
+
|
|
192
285
|
/* __axis_group_reduce__(group_axes, bundles, op) — group-reduces self along
|
|
193
286
|
* the union of `group_axes` (ascending source-axis indices = the slab) into
|
|
194
287
|
* composite groups described by `bundles`, preserving the band (= non-grouped)
|
|
@@ -240,7 +333,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
240
333
|
int8_t axes[CA_RANK_MAX];
|
|
241
334
|
char is_group[CA_RANK_MAX];
|
|
242
335
|
for ( int8_t i = 0; i < src->ndim; i++ ) is_group[i] = 0;
|
|
243
|
-
ca_size_t group_prod = 1;
|
|
244
336
|
for ( long i = 0; i < ngroup; i++ ) {
|
|
245
337
|
int a = NUM2INT(RARRAY_AREF(vgaxes, i));
|
|
246
338
|
if ( a < 0 || a >= src->ndim ) {
|
|
@@ -254,7 +346,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
254
346
|
}
|
|
255
347
|
is_group[a] = 1;
|
|
256
348
|
axes[i] = (int8_t) a;
|
|
257
|
-
group_prod *= src->dim[a];
|
|
258
349
|
}
|
|
259
350
|
|
|
260
351
|
/* --- bundles: small per-group code tables (metadata, kept alive) --- */
|
|
@@ -288,12 +379,25 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
288
379
|
rb_raise(rb_eArgError, "axis_group_reduce: bundle k must be positive");
|
|
289
380
|
}
|
|
290
381
|
|
|
291
|
-
/*
|
|
382
|
+
/* The code table is copied out and the source let go of at once, rather
|
|
383
|
+
than held attached for the length of the walk. Everything between here
|
|
384
|
+
and the end of the walk can raise -- a validation just below, a gather
|
|
385
|
+
that cannot convert a cell, an object-lane callback into Ruby -- and a
|
|
386
|
+
raise jumps over every detach, leaving the source materialised with
|
|
387
|
+
nothing left to release it. The copy rides a temporary buffer that the
|
|
388
|
+
unwind collects. */
|
|
292
389
|
VALUE v32 = rb_ca_wrap_readonly(vcodes, INT2NUM(CA_INT32));
|
|
293
390
|
rb_ary_push((VALUE) keep, v32);
|
|
294
391
|
GetCArray(v32, bundle_ca[bi]);
|
|
295
|
-
|
|
296
|
-
|
|
392
|
+
{
|
|
393
|
+
size_t nbytes = (size_t) bundle_ca[bi]->elements * sizeof(int32_t);
|
|
394
|
+
VALUE vbuf = rb_str_tmp_new((long) nbytes);
|
|
395
|
+
rb_ary_push((VALUE) keep, vbuf);
|
|
396
|
+
ca_attach(bundle_ca[bi]);
|
|
397
|
+
memcpy(RSTRING_PTR(vbuf), bundle_ca[bi]->ptr, nbytes);
|
|
398
|
+
ca_detach(bundle_ca[bi]);
|
|
399
|
+
bundle_codes[bi] = (int32_t *) RSTRING_PTR(vbuf);
|
|
400
|
+
}
|
|
297
401
|
|
|
298
402
|
int nb = (int) RARRAY_LEN(vbaxes);
|
|
299
403
|
if ( nb <= 0 || nb > src->ndim ) {
|
|
@@ -348,63 +452,88 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
348
452
|
}
|
|
349
453
|
}
|
|
350
454
|
|
|
351
|
-
/* --- band layout + output shape [K_total, *band_dims] ---
|
|
352
|
-
|
|
455
|
+
/* --- band layout + output shape [K_total, *band_dims] ---
|
|
456
|
+
band is the product of the band dims, taken directly rather than as
|
|
457
|
+
src->elements / group_prod: a zero-length *group* axis makes group_prod
|
|
458
|
+
zero, and the division has to answer something. Answering 0 made nout 0
|
|
459
|
+
while the output really had K_total x band cells, so every finalisation
|
|
460
|
+
loop below ran zero times and the freshly zeroed buffer went back to the
|
|
461
|
+
caller unmasked -- a mean, a min and a variance all reported as 0.0.
|
|
462
|
+
Taken directly, nout == co->elements for every shape, and the group with
|
|
463
|
+
no cell in it takes the same empty-group path as any other. */
|
|
464
|
+
ca_size_t band = 1;
|
|
353
465
|
ca_size_t odim[CA_RANK_MAX];
|
|
354
466
|
int8_t ondim = 1;
|
|
355
467
|
odim[0] = K_total;
|
|
356
468
|
for ( int8_t i = 0; i < src->ndim; i++ ) {
|
|
357
|
-
if ( ! is_group[i] ) odim[ondim++] = src->dim[i];
|
|
469
|
+
if ( ! is_group[i] ) { odim[ondim++] = src->dim[i]; band *= src->dim[i]; }
|
|
358
470
|
}
|
|
359
471
|
ca_size_t nout = K_total * band;
|
|
360
472
|
|
|
361
|
-
/* --- supported
|
|
473
|
+
/* --- supported data type gate (before any allocation) --- */
|
|
362
474
|
ca = src;
|
|
363
475
|
switch ( src->data_type ) {
|
|
364
476
|
case CA_BOOLEAN: case CA_INT8: case CA_UINT8: case CA_INT16: case CA_UINT16:
|
|
365
477
|
case CA_INT32: case CA_UINT32: case CA_INT64: case CA_UINT64:
|
|
366
478
|
case CA_FLOAT32: case CA_FLOAT64: break;
|
|
367
479
|
default:
|
|
368
|
-
for ( int bi = 0; bi < n_bundles; bi++ ) ca_detach(bundle_ca[bi]);
|
|
369
480
|
rb_raise(rb_eRuntimeError,
|
|
370
481
|
"axis_group_reduce: unsupported source data_type %d",
|
|
371
482
|
src->data_type);
|
|
372
483
|
}
|
|
373
484
|
|
|
374
|
-
/* output
|
|
485
|
+
/* output data type per op */
|
|
375
486
|
int8_t out_dt = CA_FLOAT64;
|
|
376
487
|
if ( op == GR_COUNT ) out_dt = CA_INT64;
|
|
377
488
|
else if ( op == GR_MINADDR || op == GR_MAXADDR ) out_dt = CA_INT64;
|
|
378
489
|
else if ( op == GR_ALL || op == GR_ANY ) out_dt = CA_BOOLEAN;
|
|
490
|
+
else if ( op == GR_ACCUM ) out_dt = src->data_type;
|
|
491
|
+
else if ( op == GR_MIN || op == GR_MAX )
|
|
492
|
+
out_dt = ( src->data_type == CA_BOOLEAN ) ? CA_UINT64 : src->data_type;
|
|
379
493
|
VALUE vout = rb_carray_new(out_dt, ondim, odim, 0, NULL);
|
|
380
494
|
GetCArray(vout, co);
|
|
381
495
|
|
|
382
496
|
/* --- accumulator buffers (only those the op needs; all O(nout)) --- */
|
|
383
|
-
|
|
384
|
-
|
|
497
|
+
/* Scratch that the unwind collects. A raise anywhere in the walk -- an
|
|
498
|
+
object-lane callback into Ruby, a gather that cannot convert a cell --
|
|
499
|
+
jumps over every release below, so these ride ALLOCV rather than plain
|
|
500
|
+
malloc: the temporary buffer behind each holder is freed whether the
|
|
501
|
+
function returns or unwinds. */
|
|
502
|
+
volatile VALUE h_cnt = 0, h_sum = 0, h_sumsq = 0, h_prod = 0, h_seen = 0,
|
|
503
|
+
h_xbuf = 0, h_nz = 0, h_mnaddr = 0, h_mxaddr = 0, h_band = 0;
|
|
504
|
+
ca_size_t *cnt = ALLOCV_N(ca_size_t, h_cnt, nout);
|
|
505
|
+
MEMZERO(cnt, ca_size_t, nout);
|
|
506
|
+
double *sum = NULL, *sumsq = NULL, *prod = NULL;
|
|
507
|
+
boolean8_t *seen_num = NULL; /* has a number (not a NaN) landed here? */
|
|
508
|
+
char *xbuf = NULL; /* running extremum, in the source type */
|
|
385
509
|
ca_size_t *nz = NULL;
|
|
386
510
|
int64_t *mnaddr = NULL, *mxaddr = NULL; /* flat source addr of min / max */
|
|
387
511
|
ca_size_t *band_addr = NULL; /* raveled addr of each band cell */
|
|
388
512
|
if ( op == GR_SUM || op == GR_MEAN || op == GR_VARIANCE || op == GR_STDDEV ||
|
|
389
513
|
op == GR_VARIANCEP || op == GR_STDDEVP ) {
|
|
390
|
-
sum =
|
|
514
|
+
sum = ALLOCV_N(double, h_sum, nout); MEMZERO(sum, double, nout);
|
|
391
515
|
}
|
|
392
516
|
if ( op == GR_PROD ) {
|
|
393
|
-
prod =
|
|
517
|
+
prod = ALLOCV_N(double, h_prod, nout);
|
|
394
518
|
for ( ca_size_t o = 0; o < nout; o++ ) prod[o] = 1.0;
|
|
395
519
|
}
|
|
396
|
-
if ( op == GR_MIN || op ==
|
|
397
|
-
|
|
398
|
-
|
|
520
|
+
if ( op == GR_MIN || op == GR_MAX ||
|
|
521
|
+
op == GR_MINADDR || op == GR_MAXADDR ) {
|
|
522
|
+
seen_num = ALLOCV_N(boolean8_t, h_seen, nout);
|
|
523
|
+
MEMZERO(seen_num, boolean8_t, nout);
|
|
524
|
+
}
|
|
525
|
+
if ( op == GR_MIN || op == GR_MAX ) {
|
|
526
|
+
/* the walk writes the extremum straight into the output, in its own type */
|
|
527
|
+
MEMZERO(co->ptr, char, (size_t) nout * co->bytes);
|
|
399
528
|
}
|
|
400
|
-
if ( op ==
|
|
401
|
-
|
|
402
|
-
|
|
529
|
+
if ( op == GR_MINADDR || op == GR_MAXADDR ) {
|
|
530
|
+
xbuf = ALLOCV_N(char, h_xbuf, (size_t) nout * src->bytes);
|
|
531
|
+
MEMZERO(xbuf, char, (size_t) nout * src->bytes);
|
|
403
532
|
}
|
|
404
|
-
if ( op == GR_MINADDR ) { mnaddr =
|
|
405
|
-
if ( op == GR_MAXADDR ) { mxaddr =
|
|
533
|
+
if ( op == GR_MINADDR ) { mnaddr = ALLOCV_N(int64_t, h_mnaddr, nout); MEMZERO(mnaddr, int64_t, nout); }
|
|
534
|
+
if ( op == GR_MAXADDR ) { mxaddr = ALLOCV_N(int64_t, h_mxaddr, nout); MEMZERO(mxaddr, int64_t, nout); }
|
|
406
535
|
if ( op == GR_ALL || op == GR_ANY ) {
|
|
407
|
-
nz =
|
|
536
|
+
nz = ALLOCV_N(ca_size_t, h_nz, nout); MEMZERO(nz, ca_size_t, nout);
|
|
408
537
|
}
|
|
409
538
|
|
|
410
539
|
/* min_addr / max_addr need the flat raveled source address of each cell.
|
|
@@ -420,7 +549,7 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
420
549
|
int band_axis[CA_RANK_MAX]; int nband_axes = 0;
|
|
421
550
|
for ( int8_t k = 0; k < src->ndim; k++ )
|
|
422
551
|
if ( ! is_group[k] ) band_axis[nband_axes++] = k;
|
|
423
|
-
band_addr =
|
|
552
|
+
band_addr = ALLOCV_N(ca_size_t, h_band, band > 0 ? band : 1);
|
|
424
553
|
for ( ca_size_t bb = 0; bb < band; bb++ ) {
|
|
425
554
|
ca_size_t rem = bb, addr = 0;
|
|
426
555
|
for ( int j = nband_axes - 1; j >= 0; j-- ) { /* last band axis fastest */
|
|
@@ -432,7 +561,7 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
432
561
|
}
|
|
433
562
|
}
|
|
434
563
|
|
|
435
|
-
/* --- compute pass(es), native
|
|
564
|
+
/* --- compute pass(es), native data type dispatch, no forced float64 cast ---
|
|
436
565
|
variance / stddev use a centred two-pass (= matches CArray's own
|
|
437
566
|
variance, avoids the one-pass sumsq cancellation that breaks ε-close
|
|
438
567
|
for small near-constant groups). Pass 1 fills sum + cnt; sum is then
|
|
@@ -443,34 +572,35 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
443
572
|
GROUP_DISPATCH( cnt[o] += 1; sum[o] += v; );
|
|
444
573
|
for ( ca_size_t o = 0; o < nout; o++ )
|
|
445
574
|
if ( cnt[o] > 0 ) sum[o] /= (double) cnt[o]; /* sum -> mean */
|
|
446
|
-
sumsq =
|
|
575
|
+
sumsq = ALLOCV_N(double, h_sumsq, nout); MEMZERO(sumsq, double, nout);
|
|
447
576
|
GROUP_DISPATCH( { double _d = v - sum[o]; sumsq[o] += _d * _d; } );
|
|
448
577
|
}
|
|
449
|
-
else if ( op == GR_MINADDR ) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
);
|
|
578
|
+
else if ( op == GR_MINADDR ) { GROUP_DISPATCH_T(GMINADDR); }
|
|
579
|
+
else if ( op == GR_MAXADDR ) { GROUP_DISPATCH_T(GMAXADDR); }
|
|
580
|
+
else if ( op == GR_MIN ) { GROUP_DISPATCH_EXTREMUM(<); }
|
|
581
|
+
else if ( op == GR_MAX ) { GROUP_DISPATCH_EXTREMUM(>); }
|
|
582
|
+
else if ( op == GR_ACCUM ) {
|
|
583
|
+
MEMZERO(co->ptr, char, (size_t) nout * co->bytes);
|
|
584
|
+
switch ( ca->data_type ) {
|
|
585
|
+
case CA_BOOLEAN: GROUP_WALK(boolean8_t, GACC_XOR); break;
|
|
586
|
+
case CA_INT8: GROUP_WALK(int8_t, GACC_ADD(int8_t)); break;
|
|
587
|
+
case CA_UINT8: GROUP_WALK(uint8_t, GACC_ADD(uint8_t)); break;
|
|
588
|
+
case CA_INT16: GROUP_WALK(int16_t, GACC_ADD(int16_t)); break;
|
|
589
|
+
case CA_UINT16: GROUP_WALK(uint16_t, GACC_ADD(uint16_t)); break;
|
|
590
|
+
case CA_INT32: GROUP_WALK(int32_t, GACC_ADD(int32_t)); break;
|
|
591
|
+
case CA_UINT32: GROUP_WALK(uint32_t, GACC_ADD(uint32_t)); break;
|
|
592
|
+
case CA_INT64: GROUP_WALK(int64_t, GACC_ADD(int64_t)); break;
|
|
593
|
+
case CA_UINT64: GROUP_WALK(uint64_t, GACC_ADD(uint64_t)); break;
|
|
594
|
+
case CA_FLOAT32: GROUP_WALK(float, GACC_ADD(float)); break;
|
|
595
|
+
case CA_FLOAT64: GROUP_WALK(double, GACC_ADD(double)); break;
|
|
596
|
+
default: break;
|
|
597
|
+
}
|
|
466
598
|
}
|
|
467
599
|
else {
|
|
468
600
|
GROUP_DISPATCH(
|
|
469
601
|
cnt[o] += 1;
|
|
470
602
|
if ( sum ) sum[o] += v;
|
|
471
603
|
if ( prod ) prod[o] *= v;
|
|
472
|
-
if ( mn ) { if ( v < mn[o] ) mn[o] = v; }
|
|
473
|
-
if ( mx ) { if ( v > mx[o] ) mx[o] = v; }
|
|
474
604
|
if ( nz ) { if ( v != 0.0 ) nz[o] += 1; }
|
|
475
605
|
);
|
|
476
606
|
}
|
|
@@ -498,10 +628,20 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
498
628
|
int64_t *out = (int64_t *) co->ptr;
|
|
499
629
|
int64_t *addr = ( op == GR_MINADDR ) ? mnaddr : mxaddr;
|
|
500
630
|
for ( ca_size_t o = 0; o < nout; o++ ) {
|
|
501
|
-
|
|
631
|
+
/* Nothing present, or nothing but NaN: either way no cell here won,
|
|
632
|
+
and the core answers UNDEF for both. */
|
|
633
|
+
if ( cnt[o] == 0 || ! seen_num[o] ) { out[o] = 0; MARK_UNDEF(o); }
|
|
502
634
|
else out[o] = addr[o];
|
|
503
635
|
}
|
|
504
636
|
}
|
|
637
|
+
else if ( op == GR_ACCUM ) {
|
|
638
|
+
/* already folded in place, in the source's own type; empty groups hold 0 */
|
|
639
|
+
}
|
|
640
|
+
else if ( op == GR_MIN || op == GR_MAX ) {
|
|
641
|
+
/* the walk wrote the extremum in place, in the source's own type; a group
|
|
642
|
+
with no cell in it has nothing to report */
|
|
643
|
+
for ( ca_size_t o = 0; o < nout; o++ ) if ( cnt[o] == 0 ) MARK_UNDEF(o);
|
|
644
|
+
}
|
|
505
645
|
else if ( op == GR_ALL ) {
|
|
506
646
|
boolean8_t *out = (boolean8_t *) co->ptr; /* empty -> true (vacuous) */
|
|
507
647
|
for ( ca_size_t o = 0; o < nout; o++ )
|
|
@@ -522,12 +662,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
522
662
|
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); }
|
|
523
663
|
else out[o] = sum[o] / (double) cnt[o];
|
|
524
664
|
break;
|
|
525
|
-
case GR_MIN:
|
|
526
|
-
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); } else out[o] = mn[o];
|
|
527
|
-
break;
|
|
528
|
-
case GR_MAX:
|
|
529
|
-
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); } else out[o] = mx[o];
|
|
530
|
-
break;
|
|
531
665
|
case GR_VARIANCE:
|
|
532
666
|
case GR_STDDEV:
|
|
533
667
|
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); }
|
|
@@ -546,17 +680,10 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
546
680
|
}
|
|
547
681
|
#undef MARK_UNDEF
|
|
548
682
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
if ( prod ) xfree(prod);
|
|
554
|
-
if ( mn ) xfree(mn);
|
|
555
|
-
if ( mx ) xfree(mx);
|
|
556
|
-
if ( nz ) xfree(nz);
|
|
557
|
-
if ( mnaddr ) xfree(mnaddr);
|
|
558
|
-
if ( mxaddr ) xfree(mxaddr);
|
|
559
|
-
if ( band_addr ) xfree(band_addr);
|
|
683
|
+
ALLOCV_END(h_cnt); ALLOCV_END(h_sum); ALLOCV_END(h_sumsq);
|
|
684
|
+
ALLOCV_END(h_prod); ALLOCV_END(h_seen); ALLOCV_END(h_xbuf);
|
|
685
|
+
ALLOCV_END(h_nz); ALLOCV_END(h_mnaddr); ALLOCV_END(h_mxaddr);
|
|
686
|
+
ALLOCV_END(h_band);
|
|
560
687
|
|
|
561
688
|
RB_GC_GUARD(keep);
|
|
562
689
|
return vout;
|
|
@@ -722,10 +849,10 @@ group_scan_build_plan (ca_iter_state *st, boolean8_t *m,
|
|
|
722
849
|
}
|
|
723
850
|
|
|
724
851
|
/* GROUP_SCAN_EXTREMUM_WALK(T, CMP): running extremum (cummax / cummin). The
|
|
725
|
-
extremum keeps the source
|
|
852
|
+
extremum keeps the source data type (its magnitude never grows), so it holds a
|
|
726
853
|
native T accumulator, not a widened double. The first member of a group
|
|
727
854
|
emits its own value: a per-group `seen` byte initialises the accumulator
|
|
728
|
-
lazily on first hit — no sentinel like HUGE_VAL, which an integer
|
|
855
|
+
lazily on first hit — no sentinel like HUGE_VAL, which an integer data type could
|
|
729
856
|
not represent. CMP is > for max, < for min: a later member replaces the
|
|
730
857
|
running extremum when `rv CMP acc`. A cell masked within its group holds the
|
|
731
858
|
current extremum once a member has been seen (output NOT masked, like sum);
|
|
@@ -740,7 +867,8 @@ group_scan_build_plan (ca_iter_state *st, boolean8_t *m,
|
|
|
740
867
|
boolean8_t *m; \
|
|
741
868
|
ca_size_t b = 0; \
|
|
742
869
|
int ready = 0; \
|
|
743
|
-
|
|
870
|
+
volatile VALUE h_acce = 0; /* freed by the unwind if the walk raises */ \
|
|
871
|
+
T *acce = ALLOCV_N(T, h_acce, K_total); \
|
|
744
872
|
T *outp = (T *) co->ptr; \
|
|
745
873
|
CA_FOR_EACH_SLAB(st, ca, axes, (int8_t) ngroup, CA_KERNEL_READ, p, m) { \
|
|
746
874
|
ca_size_t SE = st.slab_elements; \
|
|
@@ -773,7 +901,7 @@ group_scan_build_plan (ca_iter_state *st, boolean8_t *m,
|
|
|
773
901
|
} \
|
|
774
902
|
b++; \
|
|
775
903
|
} \
|
|
776
|
-
|
|
904
|
+
ALLOCV_END(h_acce); \
|
|
777
905
|
} while (0)
|
|
778
906
|
|
|
779
907
|
#define GROUP_SCAN_EXTREMUM_DISPATCH(CMP) \
|
|
@@ -863,18 +991,18 @@ group_scan_op_code (VALUE vop)
|
|
|
863
991
|
* its group up to and including that cell, in row-major position order along
|
|
864
992
|
* the grouped axes (per band).
|
|
865
993
|
|
|
866
|
-
op / output
|
|
994
|
+
op / output data type:
|
|
867
995
|
:cumsum -> float64, inclusive within-group running sum.
|
|
868
996
|
:cumprod -> float64, inclusive within-group running product (init 1.0;
|
|
869
997
|
float64 like cumsum since the product grows).
|
|
870
|
-
:cummax -> source
|
|
871
|
-
grow magnitude, so the
|
|
872
|
-
:cummin -> source
|
|
998
|
+
:cummax -> source data type, running within-group maximum (extrema do not
|
|
999
|
+
grow magnitude, so the data type is preserved; int stays int).
|
|
1000
|
+
:cummin -> source data type, running within-group minimum.
|
|
873
1001
|
:cumcount -> int64, 1-based within-group running count of present cells
|
|
874
1002
|
(matching the core CArray#cumcount): the first present member
|
|
875
1003
|
of a group emits 1, the next 2, ...
|
|
876
1004
|
cumsum / cumprod keep float64 (matching the reduce siblings sum / prod);
|
|
877
|
-
integer-preserving sum / prod is a deliberate non-goal (overflow /
|
|
1005
|
+
integer-preserving sum / prod is a deliberate non-goal (overflow / data type
|
|
878
1006
|
consistency), as on the reduce side. A CA_OBJECT source emits a CA_OBJECT
|
|
879
1007
|
result for cumsum / cumprod / cummax / cummin (cumcount stays int64).
|
|
880
1008
|
|
|
@@ -954,11 +1082,25 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
954
1082
|
rb_raise(rb_eArgError, "axis_group_scan: bundle k must be positive");
|
|
955
1083
|
}
|
|
956
1084
|
|
|
1085
|
+
/* The code table is copied out and the source let go of at once, rather
|
|
1086
|
+
than held attached for the length of the walk. Everything between here
|
|
1087
|
+
and the end of the walk can raise -- a validation just below, a gather
|
|
1088
|
+
that cannot convert a cell, an object-lane callback into Ruby -- and a
|
|
1089
|
+
raise jumps over every detach, leaving the source materialised with
|
|
1090
|
+
nothing left to release it. The copy rides a temporary buffer that the
|
|
1091
|
+
unwind collects. */
|
|
957
1092
|
VALUE v32 = rb_ca_wrap_readonly(vcodes, INT2NUM(CA_INT32));
|
|
958
1093
|
rb_ary_push((VALUE) keep, v32);
|
|
959
1094
|
GetCArray(v32, bundle_ca[bi]);
|
|
960
|
-
|
|
961
|
-
|
|
1095
|
+
{
|
|
1096
|
+
size_t nbytes = (size_t) bundle_ca[bi]->elements * sizeof(int32_t);
|
|
1097
|
+
VALUE vbuf = rb_str_tmp_new((long) nbytes);
|
|
1098
|
+
rb_ary_push((VALUE) keep, vbuf);
|
|
1099
|
+
ca_attach(bundle_ca[bi]);
|
|
1100
|
+
memcpy(RSTRING_PTR(vbuf), bundle_ca[bi]->ptr, nbytes);
|
|
1101
|
+
ca_detach(bundle_ca[bi]);
|
|
1102
|
+
bundle_codes[bi] = (int32_t *) RSTRING_PTR(vbuf);
|
|
1103
|
+
}
|
|
962
1104
|
|
|
963
1105
|
int nb = (int) RARRAY_LEN(vbaxes);
|
|
964
1106
|
if ( nb <= 0 || nb > src->ndim ) {
|
|
@@ -1011,21 +1153,20 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1011
1153
|
|
|
1012
1154
|
ca_size_t band = (group_prod > 0) ? (src->elements / group_prod) : 0;
|
|
1013
1155
|
|
|
1014
|
-
/* --- supported
|
|
1156
|
+
/* --- supported data type gate (CA_OBJECT handled by its own lane below) --- */
|
|
1015
1157
|
ca = src;
|
|
1016
1158
|
switch ( src->data_type ) {
|
|
1017
1159
|
case CA_BOOLEAN: case CA_INT8: case CA_UINT8: case CA_INT16: case CA_UINT16:
|
|
1018
1160
|
case CA_INT32: case CA_UINT32: case CA_INT64: case CA_UINT64:
|
|
1019
1161
|
case CA_FLOAT32: case CA_FLOAT64: case CA_OBJECT: break;
|
|
1020
1162
|
default:
|
|
1021
|
-
for ( int bi = 0; bi < n_bundles; bi++ ) ca_detach(bundle_ca[bi]);
|
|
1022
1163
|
rb_raise(rb_eRuntimeError,
|
|
1023
1164
|
"axis_group_scan: unsupported source data_type %d",
|
|
1024
1165
|
src->data_type);
|
|
1025
1166
|
}
|
|
1026
1167
|
|
|
1027
|
-
/* --- output
|
|
1028
|
-
source -> object); cummax / cummin preserve the source
|
|
1168
|
+
/* --- output data type per op: cumcount int64; cumsum / cumprod float64 (object
|
|
1169
|
+
source -> object); cummax / cummin preserve the source data type (object ->
|
|
1029
1170
|
object). --- */
|
|
1030
1171
|
int8_t out_dt;
|
|
1031
1172
|
if ( op == GS_CUMCOUNT ) { out_dt = CA_INT64; }
|
|
@@ -1055,7 +1196,8 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1055
1196
|
int band_axis[CA_RANK_MAX]; int nband_axes = 0;
|
|
1056
1197
|
for ( int8_t k = 0; k < src->ndim; k++ )
|
|
1057
1198
|
if ( ! is_group[k] ) band_axis[nband_axes++] = k;
|
|
1058
|
-
|
|
1199
|
+
volatile VALUE h_band2 = 0;
|
|
1200
|
+
ca_size_t *band_addr = ALLOCV_N(ca_size_t, h_band2, band > 0 ? band : 1);
|
|
1059
1201
|
for ( ca_size_t bb = 0; bb < band; bb++ ) {
|
|
1060
1202
|
ca_size_t rem = bb, addr = 0;
|
|
1061
1203
|
for ( int j = nband_axes - 1; j >= 0; j-- ) {
|
|
@@ -1072,13 +1214,20 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1072
1214
|
member / object identity init) are all tiny; acco (object running VALUE) is
|
|
1073
1215
|
allocated only for the object arithmetic / extremum ops. */
|
|
1074
1216
|
ca_size_t plan_n = (group_prod > 0) ? group_prod : 1;
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1217
|
+
/* Scratch that the unwind collects. The object lane calls back into Ruby
|
|
1218
|
+
for every cell, so a raise in the middle of the walk is ordinary here --
|
|
1219
|
+
an operand that will not coerce, a <=> that answers nil -- and it jumps
|
|
1220
|
+
over every release below. ALLOCV frees the buffer behind each holder
|
|
1221
|
+
whether the function returns or unwinds. */
|
|
1222
|
+
volatile VALUE h_code = 0, h_doff = 0, h_moff = 0, h_addr = 0,
|
|
1223
|
+
h_accd = 0, h_accn = 0, h_seen = 0, h_acco = 0;
|
|
1224
|
+
ca_size_t *sw_code = ALLOCV_N(ca_size_t, h_code, plan_n);
|
|
1225
|
+
ca_size_t *sw_doff = ALLOCV_N(ca_size_t, h_doff, plan_n);
|
|
1226
|
+
ca_size_t *sw_moff = ALLOCV_N(ca_size_t, h_moff, plan_n);
|
|
1227
|
+
ca_size_t *sw_addr = ALLOCV_N(ca_size_t, h_addr, plan_n);
|
|
1228
|
+
double *accd = ALLOCV_N(double, h_accd, K_total);
|
|
1229
|
+
ca_size_t *accn = ALLOCV_N(ca_size_t, h_accn, K_total);
|
|
1230
|
+
char *seen = ALLOCV_N(char, h_seen, K_total);
|
|
1082
1231
|
VALUE *acco = NULL;
|
|
1083
1232
|
double acc_init = ( op == GS_CUMPROD ) ? 1.0 : 0.0;
|
|
1084
1233
|
|
|
@@ -1101,7 +1250,7 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1101
1250
|
}
|
|
1102
1251
|
else { /* object running VALUE */
|
|
1103
1252
|
VALUE *outo = (VALUE *) co->ptr;
|
|
1104
|
-
acco =
|
|
1253
|
+
acco = ALLOCV_N(VALUE, h_acco, K_total);
|
|
1105
1254
|
switch ( op ) {
|
|
1106
1255
|
case GS_CUMSUM:
|
|
1107
1256
|
/* Identity 0 (matching the core object cumsum): a group's acc is lazily
|
|
@@ -1153,7 +1302,7 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1153
1302
|
}
|
|
1154
1303
|
}
|
|
1155
1304
|
}
|
|
1156
|
-
else { /* native
|
|
1305
|
+
else { /* native data type dispatch */
|
|
1157
1306
|
switch ( op ) {
|
|
1158
1307
|
case GS_CUMSUM: {
|
|
1159
1308
|
double *outd = (double *) co->ptr;
|
|
@@ -1183,16 +1332,9 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1183
1332
|
}
|
|
1184
1333
|
#undef MARK_OUT_UNDEF
|
|
1185
1334
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
xfree(sw_doff);
|
|
1190
|
-
xfree(sw_moff);
|
|
1191
|
-
xfree(sw_addr);
|
|
1192
|
-
xfree(accd);
|
|
1193
|
-
xfree(accn);
|
|
1194
|
-
xfree(seen);
|
|
1195
|
-
if ( acco ) xfree(acco);
|
|
1335
|
+
ALLOCV_END(h_band2); ALLOCV_END(h_code); ALLOCV_END(h_doff);
|
|
1336
|
+
ALLOCV_END(h_moff); ALLOCV_END(h_addr); ALLOCV_END(h_accd);
|
|
1337
|
+
ALLOCV_END(h_accn); ALLOCV_END(h_seen); ALLOCV_END(h_acco);
|
|
1196
1338
|
|
|
1197
1339
|
RB_GC_GUARD(keep);
|
|
1198
1340
|
return vout;
|