carray 3.0.1 → 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/CHANGELOG.md +520 -0
- data/README.md +2 -2
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +33 -4
- data/ext/ca_axis_group.c +202 -96
- data/ext/ca_categorical_iterator.c +108 -54
- data/ext/ca_kernel_iterator.c +317 -51
- data/ext/ca_kernel_iterator.h +142 -35
- data/ext/ca_obj_array.c +62 -20
- data/ext/ca_obj_block.c +4 -4
- data/ext/ca_obj_const_string.c +85 -26
- data/ext/ca_obj_face.c +24 -0
- data/ext/ca_obj_face.h +15 -0
- data/ext/ca_obj_fixlen_string.c +18 -5
- data/ext/ca_obj_meld.c +123 -25
- data/ext/ca_obj_object.c +8 -0
- data/ext/ca_obj_select.c +49 -34
- data/ext/ca_obj_stack.c +3 -8
- data/ext/ca_obj_stride.c +72 -1
- data/ext/ca_obj_string.c +8 -4
- data/ext/ca_obj_window.c +8 -2
- data/ext/ca_op_ipower.c +1 -2
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sweep_engine.c +307 -143
- data/ext/ca_sweep_engine.h +26 -5
- data/ext/carray.h +21 -2
- data/ext/carray_access.c +32 -20
- data/ext/carray_address_basis.c +590 -0
- data/ext/carray_broadcast.c +3 -3
- data/ext/carray_call_cfunc.c +667 -483
- data/ext/carray_cast.c +115 -41
- data/ext/carray_copy.c +55 -30
- data/ext/carray_core.c +83 -3
- data/ext/carray_count.c +9 -10
- data/ext/carray_factorize.c +46 -25
- data/ext/carray_internal.h +17 -0
- data/ext/carray_kernels_reduce_aggregate.c +168 -0
- data/ext/carray_kernels_reduce_cumulative.c +270 -1
- data/ext/carray_kernels_reduce_extreme.c +554 -8
- data/ext/carray_kernels_scan.c +4 -4
- data/ext/carray_kernels_search.c +94 -14
- data/ext/carray_loop.c +7 -1
- data/ext/carray_mask.c +23 -8
- data/ext/carray_median_percentile.c +55 -0
- data/ext/carray_operator.c +4 -4
- data/ext/carray_order.c +1 -1
- data/ext/carray_random.c +384 -40
- data/ext/carray_slab.c +13 -0
- data/ext/carray_sort.c +20 -22
- data/ext/mk_call_cfunc.rb +103 -116
- data/ext/mkkernel.rb +297 -29
- data/ext/ruby_carray.c +10 -1
- data/ext/version.h +4 -4
- data/lib/carray/autoload_carray.rb +5 -3
- data/lib/carray/autoload_method_extension.rb +12 -0
- data/lib/carray/axis_group.rb +77 -0
- data/lib/carray/basics.rb +4 -0
- data/lib/carray/block_iterator.rb +92 -16
- data/lib/carray/categorical.rb +150 -33
- data/lib/carray/categorical_iterator.rb +207 -80
- data/lib/carray/const_string.rb +131 -27
- data/lib/carray/construct.rb +40 -0
- data/lib/carray/data_type_extension.rb +3 -0
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/csv_parser.rb +11 -4
- data/lib/carray/frame/frame.rb +81 -10
- data/lib/carray/frame/group.rb +36 -3
- data/lib/carray/frame/io.rb +67 -15
- data/lib/carray/frame/records.rb +18 -4
- data/lib/carray/frame/verbs.rb +14 -11
- data/lib/carray/inspect.rb +42 -9
- data/lib/carray/iterator.rb +143 -0
- data/lib/carray/lazy.rb +0 -37
- data/lib/carray/mask_gap_fill.rb +3 -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 +13 -2
- data/lib/carray/methods/locate_addr.rb +75 -2
- data/lib/carray/methods/mask_duplicates.rb +35 -1
- data/lib/carray/methods/nunique.rb +22 -1
- data/lib/carray/methods/repeat.rb +110 -0
- data/lib/carray/methods/unique.rb +41 -1
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/slab_iterator.rb +58 -13
- data/lib/carray/string_operation_extension.rb +5 -1
- data/lib/carray/time.rb +18 -2
- data/lib/carray/window_iterator.rb +142 -20
- data/lib/carray.rb +2 -0
- data/yard-stubs/ca_obj_block.rb +2 -7
- data/yard-stubs/ca_obj_window.rb +10 -2
- data/yard-stubs/carray_access.rb +1 -1
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_core.rb +0 -80
- data/yard-stubs/carray_count.rb +7 -2
- data/yard-stubs/carray_lazy.rb +205 -0
- data/yard-stubs/carray_math.rb +1486 -3
- data/yard-stubs/carray_median_percentile.rb +16 -2
- data/yard-stubs/carray_order.rb +9 -69
- data/yard-stubs/carray_slab.rb +9 -7
- data/yard-stubs/carray_sort.rb +7 -5
- metadata +9 -1
data/ext/ca_axis_group.c
CHANGED
|
@@ -110,16 +110,20 @@ group_op_code (VALUE vop)
|
|
|
110
110
|
ca_size_t *gw_ocode = NULL, *gw_doff = NULL, *gw_moff = NULL, \
|
|
111
111
|
*gw_gaddr = NULL; \
|
|
112
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; \
|
|
113
117
|
int gw_need_addr = ( op == GR_MINADDR || op == GR_MAXADDR ); \
|
|
114
118
|
CA_FOR_EACH_SLAB(st, ca, axes, (int8_t) ngroup, CA_KERNEL_READ, p, m) { \
|
|
115
119
|
int8_t sndim = st.slab_ndim; \
|
|
116
120
|
ca_size_t SE = st.slab_elements; \
|
|
117
121
|
if ( SE > 0 ) { \
|
|
118
122
|
if ( ! gw_ready ) { \
|
|
119
|
-
gw_ocode =
|
|
120
|
-
gw_doff =
|
|
121
|
-
gw_moff =
|
|
122
|
-
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); \
|
|
123
127
|
ca_size_t sidx[CA_RANK_MAX]; \
|
|
124
128
|
for ( int8_t k = 0; k < sndim; k++ ) sidx[k] = 0; \
|
|
125
129
|
for ( ca_size_t e = 0; e < SE; e++ ) { \
|
|
@@ -158,21 +162,95 @@ group_op_code (VALUE vop)
|
|
|
158
162
|
ca_size_t oc = gw_ocode[e]; \
|
|
159
163
|
if ( oc == GW_SKIP ) continue; \
|
|
160
164
|
if ( m && m[ gw_moff[e] ] ) continue; \
|
|
161
|
-
|
|
165
|
+
T rv = *(T *)(p + gw_doff[e]); \
|
|
166
|
+
double v = (double) rv; \
|
|
162
167
|
ca_size_t o = oc + b; \
|
|
163
168
|
ca_size_t gaddr = gw_gaddr[e]; \
|
|
164
169
|
ACCUM; \
|
|
165
|
-
(void) v; (void) o; (void) gaddr;
|
|
170
|
+
(void) rv; (void) v; (void) o; (void) gaddr; \
|
|
166
171
|
} \
|
|
167
172
|
} \
|
|
168
173
|
b++; \
|
|
169
174
|
} \
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if ( gw_moff ) xfree(gw_moff); \
|
|
173
|
-
if ( gw_gaddr ) xfree(gw_gaddr); \
|
|
175
|
+
ALLOCV_END(gw_h1); ALLOCV_END(gw_h2); \
|
|
176
|
+
ALLOCV_END(gw_h3); ALLOCV_END(gw_h4); \
|
|
174
177
|
} while (0)
|
|
175
178
|
|
|
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
|
+
|
|
176
254
|
/* Run one walk over every supported native data type. Dispatched on the source
|
|
177
255
|
data_type so the inner loop stays monomorphic (no forced float64 cast). */
|
|
178
256
|
#define GROUP_DISPATCH(ACCUM) \
|
|
@@ -255,7 +333,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
255
333
|
int8_t axes[CA_RANK_MAX];
|
|
256
334
|
char is_group[CA_RANK_MAX];
|
|
257
335
|
for ( int8_t i = 0; i < src->ndim; i++ ) is_group[i] = 0;
|
|
258
|
-
ca_size_t group_prod = 1;
|
|
259
336
|
for ( long i = 0; i < ngroup; i++ ) {
|
|
260
337
|
int a = NUM2INT(RARRAY_AREF(vgaxes, i));
|
|
261
338
|
if ( a < 0 || a >= src->ndim ) {
|
|
@@ -269,7 +346,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
269
346
|
}
|
|
270
347
|
is_group[a] = 1;
|
|
271
348
|
axes[i] = (int8_t) a;
|
|
272
|
-
group_prod *= src->dim[a];
|
|
273
349
|
}
|
|
274
350
|
|
|
275
351
|
/* --- bundles: small per-group code tables (metadata, kept alive) --- */
|
|
@@ -303,12 +379,25 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
303
379
|
rb_raise(rb_eArgError, "axis_group_reduce: bundle k must be positive");
|
|
304
380
|
}
|
|
305
381
|
|
|
306
|
-
/*
|
|
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. */
|
|
307
389
|
VALUE v32 = rb_ca_wrap_readonly(vcodes, INT2NUM(CA_INT32));
|
|
308
390
|
rb_ary_push((VALUE) keep, v32);
|
|
309
391
|
GetCArray(v32, bundle_ca[bi]);
|
|
310
|
-
|
|
311
|
-
|
|
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
|
+
}
|
|
312
401
|
|
|
313
402
|
int nb = (int) RARRAY_LEN(vbaxes);
|
|
314
403
|
if ( nb <= 0 || nb > src->ndim ) {
|
|
@@ -363,13 +452,21 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
363
452
|
}
|
|
364
453
|
}
|
|
365
454
|
|
|
366
|
-
/* --- band layout + output shape [K_total, *band_dims] ---
|
|
367
|
-
|
|
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;
|
|
368
465
|
ca_size_t odim[CA_RANK_MAX];
|
|
369
466
|
int8_t ondim = 1;
|
|
370
467
|
odim[0] = K_total;
|
|
371
468
|
for ( int8_t i = 0; i < src->ndim; i++ ) {
|
|
372
|
-
if ( ! is_group[i] ) odim[ondim++] = src->dim[i];
|
|
469
|
+
if ( ! is_group[i] ) { odim[ondim++] = src->dim[i]; band *= src->dim[i]; }
|
|
373
470
|
}
|
|
374
471
|
ca_size_t nout = K_total * band;
|
|
375
472
|
|
|
@@ -380,7 +477,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
380
477
|
case CA_INT32: case CA_UINT32: case CA_INT64: case CA_UINT64:
|
|
381
478
|
case CA_FLOAT32: case CA_FLOAT64: break;
|
|
382
479
|
default:
|
|
383
|
-
for ( int bi = 0; bi < n_bundles; bi++ ) ca_detach(bundle_ca[bi]);
|
|
384
480
|
rb_raise(rb_eRuntimeError,
|
|
385
481
|
"axis_group_reduce: unsupported source data_type %d",
|
|
386
482
|
src->data_type);
|
|
@@ -392,35 +488,52 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
392
488
|
else if ( op == GR_MINADDR || op == GR_MAXADDR ) out_dt = CA_INT64;
|
|
393
489
|
else if ( op == GR_ALL || op == GR_ANY ) out_dt = CA_BOOLEAN;
|
|
394
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;
|
|
395
493
|
VALUE vout = rb_carray_new(out_dt, ondim, odim, 0, NULL);
|
|
396
494
|
GetCArray(vout, co);
|
|
397
495
|
|
|
398
496
|
/* --- accumulator buffers (only those the op needs; all O(nout)) --- */
|
|
399
|
-
|
|
400
|
-
|
|
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 */
|
|
401
509
|
ca_size_t *nz = NULL;
|
|
402
510
|
int64_t *mnaddr = NULL, *mxaddr = NULL; /* flat source addr of min / max */
|
|
403
511
|
ca_size_t *band_addr = NULL; /* raveled addr of each band cell */
|
|
404
512
|
if ( op == GR_SUM || op == GR_MEAN || op == GR_VARIANCE || op == GR_STDDEV ||
|
|
405
513
|
op == GR_VARIANCEP || op == GR_STDDEVP ) {
|
|
406
|
-
sum =
|
|
514
|
+
sum = ALLOCV_N(double, h_sum, nout); MEMZERO(sum, double, nout);
|
|
407
515
|
}
|
|
408
516
|
if ( op == GR_PROD ) {
|
|
409
|
-
prod =
|
|
517
|
+
prod = ALLOCV_N(double, h_prod, nout);
|
|
410
518
|
for ( ca_size_t o = 0; o < nout; o++ ) prod[o] = 1.0;
|
|
411
519
|
}
|
|
412
|
-
if ( op == GR_MIN || op ==
|
|
413
|
-
|
|
414
|
-
|
|
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);
|
|
415
524
|
}
|
|
416
|
-
if ( op ==
|
|
417
|
-
|
|
418
|
-
|
|
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);
|
|
528
|
+
}
|
|
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);
|
|
419
532
|
}
|
|
420
|
-
if ( op == GR_MINADDR ) { mnaddr =
|
|
421
|
-
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); }
|
|
422
535
|
if ( op == GR_ALL || op == GR_ANY ) {
|
|
423
|
-
nz =
|
|
536
|
+
nz = ALLOCV_N(ca_size_t, h_nz, nout); MEMZERO(nz, ca_size_t, nout);
|
|
424
537
|
}
|
|
425
538
|
|
|
426
539
|
/* min_addr / max_addr need the flat raveled source address of each cell.
|
|
@@ -436,7 +549,7 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
436
549
|
int band_axis[CA_RANK_MAX]; int nband_axes = 0;
|
|
437
550
|
for ( int8_t k = 0; k < src->ndim; k++ )
|
|
438
551
|
if ( ! is_group[k] ) band_axis[nband_axes++] = k;
|
|
439
|
-
band_addr =
|
|
552
|
+
band_addr = ALLOCV_N(ca_size_t, h_band, band > 0 ? band : 1);
|
|
440
553
|
for ( ca_size_t bb = 0; bb < band; bb++ ) {
|
|
441
554
|
ca_size_t rem = bb, addr = 0;
|
|
442
555
|
for ( int j = nband_axes - 1; j >= 0; j-- ) { /* last band axis fastest */
|
|
@@ -459,27 +572,13 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
459
572
|
GROUP_DISPATCH( cnt[o] += 1; sum[o] += v; );
|
|
460
573
|
for ( ca_size_t o = 0; o < nout; o++ )
|
|
461
574
|
if ( cnt[o] > 0 ) sum[o] /= (double) cnt[o]; /* sum -> mean */
|
|
462
|
-
sumsq =
|
|
575
|
+
sumsq = ALLOCV_N(double, h_sumsq, nout); MEMZERO(sumsq, double, nout);
|
|
463
576
|
GROUP_DISPATCH( { double _d = v - sum[o]; sumsq[o] += _d * _d; } );
|
|
464
577
|
}
|
|
465
|
-
else if ( op == GR_MINADDR ) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
ca_size_t faddr = band_addr[b] + gaddr;
|
|
470
|
-
mn[o] = v; mnaddr[o] = (int64_t) faddr;
|
|
471
|
-
}
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
else if ( op == GR_MAXADDR ) {
|
|
475
|
-
GROUP_DISPATCH(
|
|
476
|
-
cnt[o] += 1;
|
|
477
|
-
if ( v > mx[o] ) {
|
|
478
|
-
ca_size_t faddr = band_addr[b] + gaddr;
|
|
479
|
-
mx[o] = v; mxaddr[o] = (int64_t) faddr;
|
|
480
|
-
}
|
|
481
|
-
);
|
|
482
|
-
}
|
|
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(>); }
|
|
483
582
|
else if ( op == GR_ACCUM ) {
|
|
484
583
|
MEMZERO(co->ptr, char, (size_t) nout * co->bytes);
|
|
485
584
|
switch ( ca->data_type ) {
|
|
@@ -502,8 +601,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
502
601
|
cnt[o] += 1;
|
|
503
602
|
if ( sum ) sum[o] += v;
|
|
504
603
|
if ( prod ) prod[o] *= v;
|
|
505
|
-
if ( mn ) { if ( v < mn[o] ) mn[o] = v; }
|
|
506
|
-
if ( mx ) { if ( v > mx[o] ) mx[o] = v; }
|
|
507
604
|
if ( nz ) { if ( v != 0.0 ) nz[o] += 1; }
|
|
508
605
|
);
|
|
509
606
|
}
|
|
@@ -531,13 +628,20 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
531
628
|
int64_t *out = (int64_t *) co->ptr;
|
|
532
629
|
int64_t *addr = ( op == GR_MINADDR ) ? mnaddr : mxaddr;
|
|
533
630
|
for ( ca_size_t o = 0; o < nout; o++ ) {
|
|
534
|
-
|
|
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); }
|
|
535
634
|
else out[o] = addr[o];
|
|
536
635
|
}
|
|
537
636
|
}
|
|
538
637
|
else if ( op == GR_ACCUM ) {
|
|
539
638
|
/* already folded in place, in the source's own type; empty groups hold 0 */
|
|
540
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
|
+
}
|
|
541
645
|
else if ( op == GR_ALL ) {
|
|
542
646
|
boolean8_t *out = (boolean8_t *) co->ptr; /* empty -> true (vacuous) */
|
|
543
647
|
for ( ca_size_t o = 0; o < nout; o++ )
|
|
@@ -558,12 +662,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
558
662
|
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); }
|
|
559
663
|
else out[o] = sum[o] / (double) cnt[o];
|
|
560
664
|
break;
|
|
561
|
-
case GR_MIN:
|
|
562
|
-
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); } else out[o] = mn[o];
|
|
563
|
-
break;
|
|
564
|
-
case GR_MAX:
|
|
565
|
-
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); } else out[o] = mx[o];
|
|
566
|
-
break;
|
|
567
665
|
case GR_VARIANCE:
|
|
568
666
|
case GR_STDDEV:
|
|
569
667
|
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); }
|
|
@@ -582,17 +680,10 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
582
680
|
}
|
|
583
681
|
#undef MARK_UNDEF
|
|
584
682
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
if ( prod ) xfree(prod);
|
|
590
|
-
if ( mn ) xfree(mn);
|
|
591
|
-
if ( mx ) xfree(mx);
|
|
592
|
-
if ( nz ) xfree(nz);
|
|
593
|
-
if ( mnaddr ) xfree(mnaddr);
|
|
594
|
-
if ( mxaddr ) xfree(mxaddr);
|
|
595
|
-
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);
|
|
596
687
|
|
|
597
688
|
RB_GC_GUARD(keep);
|
|
598
689
|
return vout;
|
|
@@ -776,7 +867,8 @@ group_scan_build_plan (ca_iter_state *st, boolean8_t *m,
|
|
|
776
867
|
boolean8_t *m; \
|
|
777
868
|
ca_size_t b = 0; \
|
|
778
869
|
int ready = 0; \
|
|
779
|
-
|
|
870
|
+
volatile VALUE h_acce = 0; /* freed by the unwind if the walk raises */ \
|
|
871
|
+
T *acce = ALLOCV_N(T, h_acce, K_total); \
|
|
780
872
|
T *outp = (T *) co->ptr; \
|
|
781
873
|
CA_FOR_EACH_SLAB(st, ca, axes, (int8_t) ngroup, CA_KERNEL_READ, p, m) { \
|
|
782
874
|
ca_size_t SE = st.slab_elements; \
|
|
@@ -809,7 +901,7 @@ group_scan_build_plan (ca_iter_state *st, boolean8_t *m,
|
|
|
809
901
|
} \
|
|
810
902
|
b++; \
|
|
811
903
|
} \
|
|
812
|
-
|
|
904
|
+
ALLOCV_END(h_acce); \
|
|
813
905
|
} while (0)
|
|
814
906
|
|
|
815
907
|
#define GROUP_SCAN_EXTREMUM_DISPATCH(CMP) \
|
|
@@ -990,11 +1082,25 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
990
1082
|
rb_raise(rb_eArgError, "axis_group_scan: bundle k must be positive");
|
|
991
1083
|
}
|
|
992
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. */
|
|
993
1092
|
VALUE v32 = rb_ca_wrap_readonly(vcodes, INT2NUM(CA_INT32));
|
|
994
1093
|
rb_ary_push((VALUE) keep, v32);
|
|
995
1094
|
GetCArray(v32, bundle_ca[bi]);
|
|
996
|
-
|
|
997
|
-
|
|
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
|
+
}
|
|
998
1104
|
|
|
999
1105
|
int nb = (int) RARRAY_LEN(vbaxes);
|
|
1000
1106
|
if ( nb <= 0 || nb > src->ndim ) {
|
|
@@ -1054,7 +1160,6 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1054
1160
|
case CA_INT32: case CA_UINT32: case CA_INT64: case CA_UINT64:
|
|
1055
1161
|
case CA_FLOAT32: case CA_FLOAT64: case CA_OBJECT: break;
|
|
1056
1162
|
default:
|
|
1057
|
-
for ( int bi = 0; bi < n_bundles; bi++ ) ca_detach(bundle_ca[bi]);
|
|
1058
1163
|
rb_raise(rb_eRuntimeError,
|
|
1059
1164
|
"axis_group_scan: unsupported source data_type %d",
|
|
1060
1165
|
src->data_type);
|
|
@@ -1091,7 +1196,8 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1091
1196
|
int band_axis[CA_RANK_MAX]; int nband_axes = 0;
|
|
1092
1197
|
for ( int8_t k = 0; k < src->ndim; k++ )
|
|
1093
1198
|
if ( ! is_group[k] ) band_axis[nband_axes++] = k;
|
|
1094
|
-
|
|
1199
|
+
volatile VALUE h_band2 = 0;
|
|
1200
|
+
ca_size_t *band_addr = ALLOCV_N(ca_size_t, h_band2, band > 0 ? band : 1);
|
|
1095
1201
|
for ( ca_size_t bb = 0; bb < band; bb++ ) {
|
|
1096
1202
|
ca_size_t rem = bb, addr = 0;
|
|
1097
1203
|
for ( int j = nband_axes - 1; j >= 0; j-- ) {
|
|
@@ -1108,13 +1214,20 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1108
1214
|
member / object identity init) are all tiny; acco (object running VALUE) is
|
|
1109
1215
|
allocated only for the object arithmetic / extremum ops. */
|
|
1110
1216
|
ca_size_t plan_n = (group_prod > 0) ? group_prod : 1;
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
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);
|
|
1118
1231
|
VALUE *acco = NULL;
|
|
1119
1232
|
double acc_init = ( op == GS_CUMPROD ) ? 1.0 : 0.0;
|
|
1120
1233
|
|
|
@@ -1137,7 +1250,7 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1137
1250
|
}
|
|
1138
1251
|
else { /* object running VALUE */
|
|
1139
1252
|
VALUE *outo = (VALUE *) co->ptr;
|
|
1140
|
-
acco =
|
|
1253
|
+
acco = ALLOCV_N(VALUE, h_acco, K_total);
|
|
1141
1254
|
switch ( op ) {
|
|
1142
1255
|
case GS_CUMSUM:
|
|
1143
1256
|
/* Identity 0 (matching the core object cumsum): a group's acc is lazily
|
|
@@ -1219,16 +1332,9 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1219
1332
|
}
|
|
1220
1333
|
#undef MARK_OUT_UNDEF
|
|
1221
1334
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
xfree(sw_doff);
|
|
1226
|
-
xfree(sw_moff);
|
|
1227
|
-
xfree(sw_addr);
|
|
1228
|
-
xfree(accd);
|
|
1229
|
-
xfree(accn);
|
|
1230
|
-
xfree(seen);
|
|
1231
|
-
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);
|
|
1232
1338
|
|
|
1233
1339
|
RB_GC_GUARD(keep);
|
|
1234
1340
|
return vout;
|