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/ca_obj_string.c
CHANGED
|
@@ -192,7 +192,9 @@ ca_operation_function_t ca_string_func = {
|
|
|
192
192
|
ca_face_xfer_addrs,
|
|
193
193
|
NULL, /* fold_stride: identity Face is not foldable */
|
|
194
194
|
ca_face_xfer_stride,
|
|
195
|
-
ca_face_xfer_all
|
|
195
|
+
ca_face_xfer_all,
|
|
196
|
+
.fill_addrs = ca_face_fill_addrs,
|
|
197
|
+
.fill_stride = ca_face_fill_stride,
|
|
196
198
|
};
|
|
197
199
|
|
|
198
200
|
/* ------------------------------------------------------------------- */
|
data/ext/ca_obj_tile.c
CHANGED
|
@@ -214,10 +214,18 @@ ca_tile_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
|
214
214
|
s = ca->bytes;
|
|
215
215
|
for (k = ndim - 1; k >= 0; k--) { dstride[k] = s; s *= counts[k]; }
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
217
|
+
/* The request is over the view's addresses, so a transposed / flat request
|
|
218
|
+
is legal and must not be composed axis-by-axis; see
|
|
219
|
+
ca_xfer_stride_request_is_axis_box (carray.h). */
|
|
220
|
+
if ( ! ca_xfer_stride_request_is_axis_box(ca, starts, counts, strides) ) {
|
|
221
|
+
structural = 0;
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
for (k = 0; k < ndim; k++) {
|
|
225
|
+
if (strides[k] % tnative[k] != 0 || strides[k] / tnative[k] != 1) {
|
|
226
|
+
structural = 0;
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
221
229
|
}
|
|
222
230
|
}
|
|
223
231
|
|
|
@@ -546,7 +554,7 @@ rb_ca_tile (int argc, VALUE *argv, VALUE self)
|
|
|
546
554
|
|
|
547
555
|
{
|
|
548
556
|
VALUE obj = rb_ca_tile_new(self, reps);
|
|
549
|
-
|
|
557
|
+
CA_WRAPPER_LIFT(obj, self, ca);
|
|
550
558
|
return obj;
|
|
551
559
|
}
|
|
552
560
|
}
|
data/ext/ca_obj_time.c
CHANGED
|
@@ -293,7 +293,9 @@ ca_operation_function_t ca_time_func = {
|
|
|
293
293
|
ca_face_xfer_addrs,
|
|
294
294
|
NULL, /* fold_stride: identity Face is not foldable */
|
|
295
295
|
ca_face_xfer_stride,
|
|
296
|
-
ca_face_xfer_all
|
|
296
|
+
ca_face_xfer_all,
|
|
297
|
+
.fill_addrs = ca_face_fill_addrs,
|
|
298
|
+
.fill_stride = ca_face_fill_stride,
|
|
297
299
|
};
|
|
298
300
|
|
|
299
301
|
/* ------------------------------------------------------------------- */
|
data/ext/ca_obj_timedelta.c
CHANGED
|
@@ -273,7 +273,9 @@ ca_operation_function_t ca_timedelta_func = {
|
|
|
273
273
|
ca_face_xfer_addrs,
|
|
274
274
|
NULL, /* fold_stride */
|
|
275
275
|
ca_face_xfer_stride,
|
|
276
|
-
ca_face_xfer_all
|
|
276
|
+
ca_face_xfer_all,
|
|
277
|
+
.fill_addrs = ca_face_fill_addrs,
|
|
278
|
+
.fill_stride = ca_face_fill_stride,
|
|
277
279
|
};
|
|
278
280
|
|
|
279
281
|
/* ------------------------------------------------------------------- */
|
data/ext/ca_obj_transpose.c
CHANGED
data/ext/ca_obj_triop.c
CHANGED
|
@@ -61,12 +61,19 @@ typedef struct CATriOp {
|
|
|
61
61
|
CArray *parent; /* = op1 */
|
|
62
62
|
uint32_t attach;
|
|
63
63
|
uint8_t nosync;
|
|
64
|
+
/* ---- CAMultiParent conformance (CA_FLAG_MULTI_PARENTS): n_parents and
|
|
65
|
+
parents[] sit immediately after the CAView header, as carray.h's
|
|
66
|
+
layout convention requires, so ca_has_mask can fold over both
|
|
67
|
+
operands and build the mask on demand instead of at setup. ---- */
|
|
68
|
+
int32_t n_parents; /* always 3 */
|
|
69
|
+
CArray **parents; /* = &operands[0]; no separate allocation */
|
|
64
70
|
/* CATriOp-specific tail */
|
|
65
71
|
CArray *op2;
|
|
66
72
|
CArray *op3;
|
|
67
73
|
uint16_t op_id;
|
|
68
74
|
uint8_t op2_is_scalar;
|
|
69
75
|
uint8_t op3_is_scalar;
|
|
76
|
+
CArray *operands[3]; /* {op1, op2, op3}; what parents points at */
|
|
70
77
|
} CATriOp;
|
|
71
78
|
|
|
72
79
|
static size_t
|
|
@@ -117,7 +124,7 @@ ca_triop_setup (CATriOp *ca, CArray *op1, CArray *op2, CArray *op3,
|
|
|
117
124
|
|
|
118
125
|
ca->obj_type = CA_OBJ_TRIOP;
|
|
119
126
|
ca->data_type = out_dt;
|
|
120
|
-
ca->flags = CA_FLAG_READ_ONLY;
|
|
127
|
+
ca->flags = CA_FLAG_READ_ONLY | CA_FLAG_MULTI_PARENTS;
|
|
121
128
|
ca->ndim = op1->ndim;
|
|
122
129
|
ca->bytes = out_bytes;
|
|
123
130
|
ca->elements = op1->elements;
|
|
@@ -134,12 +141,17 @@ ca_triop_setup (CATriOp *ca, CArray *op1, CArray *op2, CArray *op3,
|
|
|
134
141
|
ca->op_id = op_id;
|
|
135
142
|
ca->op2_is_scalar = ( op2->elements == 1 && op1->elements > 1 ) ? 1 : 0;
|
|
136
143
|
ca->op3_is_scalar = ( op3->elements == 1 && op1->elements > 1 ) ? 1 : 0;
|
|
144
|
+
ca->operands[0] = op1;
|
|
145
|
+
ca->operands[1] = op2;
|
|
146
|
+
ca->operands[2] = op3;
|
|
147
|
+
ca->parents = ca->operands;
|
|
148
|
+
ca->n_parents = 3;
|
|
137
149
|
|
|
138
150
|
memcpy(ca->dim, op1->dim, op1->ndim * sizeof(ca_size_t));
|
|
139
151
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
152
|
+
/* The mask is NOT built here. ca_has_mask folds over parents[] for a
|
|
153
|
+
multi-parent view and creates it on demand, so an expression whose mask
|
|
154
|
+
nobody reads never allocates one. */
|
|
143
155
|
|
|
144
156
|
if ( ca_is_scalar(op1) && ca_is_scalar(op2) && ca_is_scalar(op3) ) {
|
|
145
157
|
ca_set_flag(ca, CA_FLAG_SCALAR);
|
|
@@ -253,7 +265,9 @@ pull_operand (CArray *op, int is_scalar, ca_size_t *starts,
|
|
|
253
265
|
one_counts[k] = 1;
|
|
254
266
|
one_strides[k] = bytes;
|
|
255
267
|
}
|
|
256
|
-
scratch =
|
|
268
|
+
scratch = ( op->data_type == CA_OBJECT )
|
|
269
|
+
? ca_lazy_arena_acquire_object(1)
|
|
270
|
+
: ca_lazy_arena_acquire(bytes);
|
|
257
271
|
ca_triop_scratch_acquire_count++;
|
|
258
272
|
ca_xfer_stride(op, one_starts, one_counts, one_strides, scratch,
|
|
259
273
|
CA_XFER_GET);
|
|
@@ -266,7 +280,9 @@ pull_operand (CArray *op, int is_scalar, ca_size_t *starts,
|
|
|
266
280
|
op_strides[k] = s;
|
|
267
281
|
s *= counts[k];
|
|
268
282
|
}
|
|
269
|
-
scratch =
|
|
283
|
+
scratch = ( op->data_type == CA_OBJECT )
|
|
284
|
+
? ca_lazy_arena_acquire_object(slab_n)
|
|
285
|
+
: ca_lazy_arena_acquire(slab_n * bytes);
|
|
270
286
|
ca_triop_scratch_acquire_count++;
|
|
271
287
|
ca_xfer_stride(op, starts, counts, op_strides, scratch, CA_XFER_GET);
|
|
272
288
|
*step_out = 1;
|
|
@@ -343,6 +359,14 @@ ca_triop_func_allocate (void *ap)
|
|
|
343
359
|
{
|
|
344
360
|
CATriOp *ca = (CATriOp *) ap;
|
|
345
361
|
ca->ptr = xmalloc(ca_length(ca));
|
|
362
|
+
/* CA_OBJECT cells are VALUEs and this buffer is about to be marked as
|
|
363
|
+
soon as the view is, so it must not be handed to the GC as raw
|
|
364
|
+
xmalloc garbage. */
|
|
365
|
+
if ( ca->data_type == CA_OBJECT ) {
|
|
366
|
+
VALUE *p = (VALUE *) ca->ptr;
|
|
367
|
+
ca_size_t i;
|
|
368
|
+
for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
|
|
369
|
+
}
|
|
346
370
|
}
|
|
347
371
|
|
|
348
372
|
static void
|
|
@@ -362,6 +386,14 @@ ca_triop_func_attach (void *ap)
|
|
|
362
386
|
s = ca->bytes;
|
|
363
387
|
for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
|
|
364
388
|
for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
|
|
389
|
+
/* CA_OBJECT cells are VALUEs and this buffer is about to be marked as
|
|
390
|
+
soon as the view is, so it must not be handed to the GC as raw
|
|
391
|
+
xmalloc garbage. */
|
|
392
|
+
if ( ca->data_type == CA_OBJECT ) {
|
|
393
|
+
VALUE *p = (VALUE *) ca->ptr;
|
|
394
|
+
ca_size_t i;
|
|
395
|
+
for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
|
|
396
|
+
}
|
|
365
397
|
ca_triop_func_xfer_stride(ca, starts, ca->dim, native, ca->ptr, CA_XFER_GET);
|
|
366
398
|
}
|
|
367
399
|
|
|
@@ -405,13 +437,26 @@ ca_triop_func_create_mask (void *ap)
|
|
|
405
437
|
has3 = ca_has_mask(op3);
|
|
406
438
|
if ( ! has1 && ! has2 && ! has3 ) return;
|
|
407
439
|
|
|
440
|
+
/* Exactly one masked operand: the answer is that operand's mask, cell
|
|
441
|
+
for cell. Share it rather than allocating a copy per node. */
|
|
442
|
+
if ( has1 + has2 + has3 == 1 ) {
|
|
443
|
+
CArray *src = has1 ? op1 : ( has2 ? op2 : op3 );
|
|
444
|
+
if ( src->elements == to->elements ) {
|
|
445
|
+
to->mask = (CArray *) ca_refer_new(src->mask, CA_BOOLEAN,
|
|
446
|
+
to->ndim, to->dim, 0, 0);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
408
451
|
to->mask = (CArray *) carray_new(CA_BOOLEAN, to->ndim, to->dim, 0, NULL);
|
|
409
452
|
dst = (boolean8_t *) to->mask->ptr;
|
|
410
453
|
n = to->elements;
|
|
411
454
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (
|
|
455
|
+
/* The masks are what is read here; attaching the operand instead
|
|
456
|
+
materialises the whole subexpression under it. */
|
|
457
|
+
if ( has1 ) ca_attach(op1->mask);
|
|
458
|
+
if ( has2 ) ca_attach(op2->mask);
|
|
459
|
+
if ( has3 ) ca_attach(op3->mask);
|
|
415
460
|
|
|
416
461
|
m1 = has1 ? (boolean8_t *) op1->mask->ptr : NULL;
|
|
417
462
|
m2 = has2 ? (boolean8_t *) op2->mask->ptr : NULL;
|
|
@@ -426,9 +471,9 @@ ca_triop_func_create_mask (void *ap)
|
|
|
426
471
|
dst[i] = (boolean8_t) ( a | b | c );
|
|
427
472
|
}
|
|
428
473
|
|
|
429
|
-
if ( has3 ) ca_detach(op3);
|
|
430
|
-
if ( has2 ) ca_detach(op2);
|
|
431
|
-
if ( has1 ) ca_detach(op1);
|
|
474
|
+
if ( has3 ) ca_detach(op3->mask);
|
|
475
|
+
if ( has2 ) ca_detach(op2->mask);
|
|
476
|
+
if ( has1 ) ca_detach(op1->mask);
|
|
432
477
|
}
|
|
433
478
|
|
|
434
479
|
ca_operation_function_t ca_triop_func = {
|
|
@@ -597,23 +642,17 @@ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
|
|
|
597
642
|
TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
|
|
598
643
|
TypedData_Get_Struct(r3, CArray, &carray_data_type, op3);
|
|
599
644
|
|
|
600
|
-
/* Step 3:
|
|
601
|
-
|
|
602
|
-
|
|
645
|
+
/* Step 3: each of op2 / op3 must match op1's element count OR be a
|
|
646
|
+
1-element CScalar (kernel walks with element_step = 0 in that case).
|
|
647
|
+
Unreachable from Ruby; see the note in ca_obj_binop.c. */
|
|
603
648
|
if ( op2->elements != op1->elements && op2->elements != 1 ) {
|
|
604
649
|
rb_raise(rb_eArgError,
|
|
605
|
-
"CATriOp:
|
|
606
|
-
"same-ndim size-1 broadcast is supported; cross-ndim "
|
|
607
|
-
"promotion is not adopted in CArray "
|
|
608
|
-
"(reshape explicitly)",
|
|
650
|
+
"CATriOp: element count mismatch on op2 (%lld vs %lld)",
|
|
609
651
|
(long long) op2->elements, (long long) op1->elements);
|
|
610
652
|
}
|
|
611
653
|
if ( op3->elements != op1->elements && op3->elements != 1 ) {
|
|
612
654
|
rb_raise(rb_eArgError,
|
|
613
|
-
"CATriOp:
|
|
614
|
-
"same-ndim size-1 broadcast is supported; cross-ndim "
|
|
615
|
-
"promotion is not adopted in CArray "
|
|
616
|
-
"(reshape explicitly)",
|
|
655
|
+
"CATriOp: element count mismatch on op3 (%lld vs %lld)",
|
|
617
656
|
(long long) op3->elements, (long long) op1->elements);
|
|
618
657
|
}
|
|
619
658
|
|
data/ext/ca_obj_window.c
CHANGED
|
@@ -334,12 +334,20 @@ ca_window_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
|
334
334
|
for (k = ndim - 1; k >= 0; k--) { dstride[k] = s; s *= counts[k]; }
|
|
335
335
|
for (k = 0; k < ndim; k++) n *= counts[k];
|
|
336
336
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
337
|
+
/* The request is over the view's addresses, so a transposed / flat request
|
|
338
|
+
is legal and must not be composed axis-by-axis; see
|
|
339
|
+
ca_xfer_stride_request_is_axis_box (carray.h). */
|
|
340
|
+
if ( ! ca_xfer_stride_request_is_axis_box(w, starts, counts, strides) ) {
|
|
341
|
+
structural = 0;
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
for (k = 0; k < ndim; k++) {
|
|
345
|
+
if ( (w->bounds[k] != CA_BOUNDS_FILL && w->bounds[k] != CA_BOUNDS_MASK)
|
|
346
|
+
|| strides[k] % wnative[k] != 0
|
|
347
|
+
|| strides[k] / wnative[k] != 1 ) {
|
|
348
|
+
structural = 0;
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
343
351
|
}
|
|
344
352
|
}
|
|
345
353
|
|
|
@@ -374,12 +382,31 @@ ca_window_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
|
374
382
|
lo[k] = l; hi[k] = h;
|
|
375
383
|
}
|
|
376
384
|
|
|
385
|
+
/* An unattached parent that ca_attach_is_alias accepts has memory to lend
|
|
386
|
+
and the attach neither allocates nor copies, so borrow it and re-enter
|
|
387
|
+
on the batched branch below. The recursion runs at most once: the
|
|
388
|
+
second entry sees parent->ptr != NULL. A parent with nothing to lend is
|
|
389
|
+
left alone — the per-row loop is the region protocol and must not be
|
|
390
|
+
traded for a whole-parent attach. */
|
|
391
|
+
if ( ! parent->ptr && ca_attach_is_alias(parent) ) {
|
|
392
|
+
ca_attach(parent);
|
|
393
|
+
if ( parent->ptr ) {
|
|
394
|
+
ca_window_func_xfer_stride(ap, starts, counts, strides, data, dir);
|
|
395
|
+
if ( dir == CA_XFER_PUT ) {
|
|
396
|
+
ca_sync(parent);
|
|
397
|
+
}
|
|
398
|
+
ca_detach(parent);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
ca_detach(parent);
|
|
402
|
+
}
|
|
403
|
+
|
|
377
404
|
/* Fast path: when the parent is attached (ptr != NULL), drive
|
|
378
405
|
ca_composite_region_* + fill_complement directly with the sub-region
|
|
379
406
|
intersection geometry. This collapses the outer per-row dispatch loop
|
|
380
407
|
(one ca_xfer_stride per inner row) into a single batched routine. An
|
|
381
|
-
unattached parent falls through to the per-row loop
|
|
382
|
-
equivalent but slower. */
|
|
408
|
+
unattached parent with nothing to lend falls through to the per-row loop
|
|
409
|
+
below, which is equivalent but slower. */
|
|
383
410
|
if ( parent->ptr ) {
|
|
384
411
|
ca_size_t any_empty = 0;
|
|
385
412
|
ca_size_t alias_parent_start[CA_RANK_MAX];
|
|
@@ -1011,10 +1038,16 @@ ca_window_func_detach (void *ap)
|
|
|
1011
1038
|
so the caller must have made it available.
|
|
1012
1039
|
|
|
1013
1040
|
CAREFUL: neither this nor ca_window_func_xfer_all may call
|
|
1014
|
-
ca_attach(parent)
|
|
1015
|
-
|
|
1016
|
-
below instead materialises a
|
|
1017
|
-
which recurses under the
|
|
1041
|
+
ca_attach(parent) on a parent that would have to materialise to answer.
|
|
1042
|
+
A transfer slot that silently does that duplicates the whole parent
|
|
1043
|
+
behind the caller's back — the cold case below instead materialises a
|
|
1044
|
+
parent-shaped scratch through ca_xfer_all, which recurses under the
|
|
1045
|
+
same rule.
|
|
1046
|
+
|
|
1047
|
+
The exception is a parent that ca_attach_is_alias accepts: there the
|
|
1048
|
+
attach hands back parent (or root) memory that already exists, so
|
|
1049
|
+
nothing is allocated or copied and the rule has nothing to protect.
|
|
1050
|
+
ca_window_func_fill_data draws the same line. */
|
|
1018
1051
|
static void
|
|
1019
1052
|
ca_window_func_run_fast_path (CAWindow *ca, char *data, int dir)
|
|
1020
1053
|
{
|
|
@@ -1049,8 +1082,44 @@ ca_window_func_xfer_all (void *ap, void *data, int dir)
|
|
|
1049
1082
|
ca_window_func_run_fast_path(ca, (char *) data, dir);
|
|
1050
1083
|
return;
|
|
1051
1084
|
}
|
|
1052
|
-
/* Cold parent
|
|
1053
|
-
|
|
1085
|
+
/* Cold parent that has memory to lend (entity, or a CAStride-family view
|
|
1086
|
+
whose composed strides alias a ptr-bearing root): borrow it. The attach
|
|
1087
|
+
costs no allocation and no copy, and on the alias path ca->parent->ptr
|
|
1088
|
+
points into the root, so a PUT lands where the scratch path would have
|
|
1089
|
+
had to copy it back. Without this the window duplicates the entire
|
|
1090
|
+
parent on every transfer, which is what an ordinary `a[nil, nil]`,
|
|
1091
|
+
`a.refer` or `a.transpose.transpose` parent used to pay. */
|
|
1092
|
+
if ( ca_attach_is_alias(ca->parent) ) {
|
|
1093
|
+
ca_attach(ca->parent);
|
|
1094
|
+
ca_window_func_run_fast_path(ca, (char *) data, dir);
|
|
1095
|
+
if ( dir == CA_XFER_PUT ) {
|
|
1096
|
+
ca_sync(ca->parent);
|
|
1097
|
+
}
|
|
1098
|
+
ca_detach(ca->parent);
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
/* Second chance: ca_attach_is_alias reads the leaf view's own strides, so
|
|
1102
|
+
it declines a chain that is only contiguous once composed (a.transpose
|
|
1103
|
+
.transpose is the plain case). ca_resolve_attached_root_via_identity
|
|
1104
|
+
folds the chain and answers the same question about the root, and when
|
|
1105
|
+
it succeeds the parent's flat byte addressing IS the root's — so the
|
|
1106
|
+
root ptr can stand in for the parent's exactly as the scratch does
|
|
1107
|
+
below, with no copy in either direction. ca_window_func_xfer_addrs
|
|
1108
|
+
already resolves its parent this way. */
|
|
1109
|
+
{
|
|
1110
|
+
CArray *root = ca_resolve_attached_root_via_identity(ca->parent);
|
|
1111
|
+
if ( root != ca->parent && root->ptr ) {
|
|
1112
|
+
CArray *parent = ca->parent;
|
|
1113
|
+
parent->ptr = root->ptr;
|
|
1114
|
+
ca_window_func_run_fast_path(ca, (char *) data, dir);
|
|
1115
|
+
parent->ptr = NULL;
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/* Cold parent with nothing to lend: materialise it into a scratch buffer
|
|
1121
|
+
via ca_xfer_all, then run the normal fast path with the scratch standing
|
|
1122
|
+
in for parent->ptr. */
|
|
1054
1123
|
{
|
|
1055
1124
|
volatile VALUE holder;
|
|
1056
1125
|
CArray *parent = ca->parent;
|
|
@@ -1296,7 +1365,7 @@ rb_ca_window_new (VALUE cary,
|
|
|
1296
1365
|
VALUE
|
|
1297
1366
|
rb_ca_window (int argc, VALUE *argv, VALUE self)
|
|
1298
1367
|
{
|
|
1299
|
-
volatile VALUE obj, ropt, rfval =
|
|
1368
|
+
volatile VALUE obj, ropt, rfval = CA_UNSPECIFIED, rbounds = Qnil, rcs;
|
|
1300
1369
|
CArray *ca;
|
|
1301
1370
|
CScalar *cs;
|
|
1302
1371
|
ca_size_t start[CA_RANK_MAX];
|
|
@@ -1334,7 +1403,7 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
|
|
|
1334
1403
|
"use fill_value: kwarg (e.g. window(-1..1, fill_value: UNDEF))");
|
|
1335
1404
|
}
|
|
1336
1405
|
|
|
1337
|
-
if ( rfval ==
|
|
1406
|
+
if ( rfval == CA_UNSPECIFIED ) {
|
|
1338
1407
|
;
|
|
1339
1408
|
}
|
|
1340
1409
|
else if ( rfval == CA_UNDEF ) {
|
|
@@ -1397,7 +1466,7 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
|
|
|
1397
1466
|
|
|
1398
1467
|
obj = rb_ca_window_new(self, start, count, bounds, fill);
|
|
1399
1468
|
|
|
1400
|
-
|
|
1469
|
+
CA_WRAPPER_LIFT(obj, self, ca);
|
|
1401
1470
|
return obj;
|
|
1402
1471
|
}
|
|
1403
1472
|
|
data/ext/ca_op_cmplx64.h
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
ca_op_cmplx64.h -- the cmplx64 products and quotients, computed in
|
|
4
|
+
double and rounded once
|
|
5
|
+
|
|
6
|
+
Used by:
|
|
7
|
+
- ext/carray_kernels_binop.c and ext/carray_kernels_monop.c
|
|
8
|
+
(generated from ext/mkkernel.rb): the `*`, `/`, `rcp` and
|
|
9
|
+
`rcp_mul` kernels for CA_CMPLX64.
|
|
10
|
+
|
|
11
|
+
Why these are not just `x * y` and `x / y`. Both the product and the
|
|
12
|
+
quotient of two complex numbers subtract two products of the parts, so
|
|
13
|
+
the result cancels whenever those two are close. Done at the operands'
|
|
14
|
+
own width there are no bits left underneath to absorb the cancellation.
|
|
15
|
+
Divide has a second cost: the compiler turns a `float _Complex` divide
|
|
16
|
+
into a call to `__divsc3`, which is Smith's algorithm (scale by the
|
|
17
|
+
larger component so the intermediate squares cannot overflow) plus the
|
|
18
|
+
C99 Annex G recovery for infinities -- branches and a call per cell.
|
|
19
|
+
|
|
20
|
+
For a cmplx64 the scaling is not needed at all: the operands are
|
|
21
|
+
floats, so a product of two parts reaches about 1.2e77 where a double
|
|
22
|
+
reaches 1.8e308. Computing the textbook formulas in double therefore
|
|
23
|
+
cannot overflow, and the double carries 29 extra mantissa bits, which
|
|
24
|
+
is what the cancellation needs. Measured against an exact rational
|
|
25
|
+
reference, the double route was correctly rounded on every sample
|
|
26
|
+
where the width-native route was off by up to 1806 ulp (divide) and
|
|
27
|
+
1679 ulp (multiply).
|
|
28
|
+
|
|
29
|
+
The two are not the same trade. Divide gets faster as well, because
|
|
30
|
+
the call and the scaling both go away. Multiply gets slower, because
|
|
31
|
+
the compiler already inlines a naive float product and only calls
|
|
32
|
+
`__mulsc3` when a NaN appears -- so the wider arithmetic is pure cost
|
|
33
|
+
there, and is paid for the accuracy alone.
|
|
34
|
+
|
|
35
|
+
Annex G is preserved by falling back rather than by reimplementing it.
|
|
36
|
+
The fallback is chosen from the *result*, not from a classification of
|
|
37
|
+
the inputs: if the quick answer is not an ordinary number, hand the
|
|
38
|
+
pair to the compiler's helper and return whatever it says. Writing an
|
|
39
|
+
input classification here would mean transcribing the Annex G table,
|
|
40
|
+
and a transcription drifts from what the runtime actually does.
|
|
41
|
+
|
|
42
|
+
cmplx128 has no wider type to borrow (`long double` is a double on
|
|
43
|
+
Apple ARM64 and a slow 80-bit on x86-64), so it keeps `__mulsc3` /
|
|
44
|
+
`__divdc3`.
|
|
45
|
+
|
|
46
|
+
--------------------------------------------------------------------------- */
|
|
47
|
+
|
|
48
|
+
#ifndef CA_OP_CMPLX64_H
|
|
49
|
+
#define CA_OP_CMPLX64_H
|
|
50
|
+
|
|
51
|
+
#include "carray.h"
|
|
52
|
+
#include <math.h>
|
|
53
|
+
|
|
54
|
+
#ifdef HAVE_COMPLEX_H
|
|
55
|
+
|
|
56
|
+
/* CMPLXF is the float sibling of the CMPLX defined in carray.h, and is
|
|
57
|
+
here for the same reason: `re + I * im` evaluates `I * im` first, so an
|
|
58
|
+
im of +0.0 loses the sign of a -0.0 real part. CMPLXF is C11; provide
|
|
59
|
+
it when the toolchain predates that. */
|
|
60
|
+
#ifndef CMPLXF
|
|
61
|
+
# if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
|
|
62
|
+
# define CMPLXF(re, im) __builtin_complex((float)(re), (float)(im))
|
|
63
|
+
# else
|
|
64
|
+
# define CMPLXF(re, im) \
|
|
65
|
+
(((union { float _parts[2]; float complex _value; }) \
|
|
66
|
+
{ { (float)(re), (float)(im) } })._value)
|
|
67
|
+
# endif
|
|
68
|
+
#endif
|
|
69
|
+
|
|
70
|
+
/* The real part is `xr*yr - xi*yi`, which cancels when the two products
|
|
71
|
+
are close; the double keeps the bits that cancellation eats. Only a
|
|
72
|
+
NaN can appear here that the naive form gets wrong, since a product of
|
|
73
|
+
two floats cannot overflow a double -- an infinite part in the answer
|
|
74
|
+
is the true answer. */
|
|
75
|
+
static inline cmplx64_t
|
|
76
|
+
op_cmul_cmplx64 (cmplx64_t x, cmplx64_t y)
|
|
77
|
+
{
|
|
78
|
+
double xr = crealf(x), xi = cimagf(x);
|
|
79
|
+
double yr = crealf(y), yi = cimagf(y);
|
|
80
|
+
float rr = (float) (xr * yr - xi * yi);
|
|
81
|
+
float ri = (float) (xr * yi + xi * yr);
|
|
82
|
+
if ( isnan(rr) || isnan(ri) ) {
|
|
83
|
+
return x * y; /* __mulsc3: an infinity met a zero (Annex G) */
|
|
84
|
+
}
|
|
85
|
+
return CMPLXF(rr, ri);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static inline cmplx64_t
|
|
89
|
+
op_cdiv_cmplx64 (cmplx64_t x, cmplx64_t y)
|
|
90
|
+
{
|
|
91
|
+
double yr = crealf(y), yi = cimagf(y);
|
|
92
|
+
double d = yr * yr + yi * yi;
|
|
93
|
+
if ( d > 0.0 && d < INFINITY ) {
|
|
94
|
+
double xr = crealf(x), xi = cimagf(x);
|
|
95
|
+
float rr = (float) ((xr * yr + xi * yi) / d);
|
|
96
|
+
float ri = (float) ((xi * yr - xr * yi) / d);
|
|
97
|
+
if ( isfinite(rr) && isfinite(ri) ) {
|
|
98
|
+
return CMPLXF(rr, ri);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return x / y; /* __divsc3: zero, infinite or NaN operands (Annex G) */
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* 1 / y. Same shape with the numerator's parts folded in, so that
|
|
105
|
+
`rcp` and `1 / z` keep giving the same answer. */
|
|
106
|
+
static inline cmplx64_t
|
|
107
|
+
op_crcp_cmplx64 (cmplx64_t y)
|
|
108
|
+
{
|
|
109
|
+
double yr = crealf(y), yi = cimagf(y);
|
|
110
|
+
double d = yr * yr + yi * yi;
|
|
111
|
+
if ( d > 0.0 && d < INFINITY ) {
|
|
112
|
+
float rr = (float) (yr / d);
|
|
113
|
+
float ri = (float) (-yi / d);
|
|
114
|
+
if ( isfinite(rr) && isfinite(ri) ) {
|
|
115
|
+
return CMPLXF(rr, ri);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return 1 / y;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#endif /* HAVE_COMPLEX_H */
|
|
122
|
+
|
|
123
|
+
#endif /* CA_OP_CMPLX64_H */
|
data/ext/ca_op_ipower.c
CHANGED
|
@@ -233,8 +233,6 @@ rb_ca_ipower (VALUE self, VALUE other)
|
|
|
233
233
|
ALLOCV_END(h1);
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
obj = rb_ca_rewrap_unbound_repeat(self, obj);
|
|
237
|
-
|
|
238
236
|
return obj;
|
|
239
237
|
}
|
|
240
238
|
|
|
@@ -289,7 +287,6 @@ static VALUE rb_ca_pow (VALUE self, VALUE other)
|
|
|
289
287
|
}
|
|
290
288
|
else {
|
|
291
289
|
obj = rb_ca_power(self, other);
|
|
292
|
-
obj = rb_ca_rewrap_unbound_repeat(self, obj);
|
|
293
290
|
return obj;
|
|
294
291
|
}
|
|
295
292
|
}
|
data/ext/ca_sort_kernels.h
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Portable textbook sort kernels (PROPOSAL_PORTABLE_TEXTBOOK_SORT).
|
|
6
6
|
|
|
7
|
-
P.1 / P.2 : quicksort + mergesort over the 10 numeric
|
|
7
|
+
P.1 / P.2 : quicksort + mergesort over the 10 numeric data types
|
|
8
8
|
(i8 / u8 / i16 / u16 / i32 / u32 / i64 / u64 / f32 / f64)
|
|
9
9
|
P.3 : NaN pre-partition for f32 / f64
|
|
10
10
|
P.4 : pair (value + index) variants for argsort kernels —
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
flight. */
|
|
34
34
|
#include "carray.h"
|
|
35
35
|
|
|
36
|
-
/* P.1 / P.2: quicksort over 10 numeric
|
|
36
|
+
/* P.1 / P.2: quicksort over 10 numeric data types. */
|
|
37
37
|
void ca_sort_quick_i8 (int8_t *a, ca_size_t n);
|
|
38
38
|
void ca_sort_quick_u8 (uint8_t *a, ca_size_t n);
|
|
39
39
|
void ca_sort_quick_i16 (int16_t *a, ca_size_t n);
|
|
@@ -45,8 +45,8 @@ void ca_sort_quick_u64 (uint64_t *a, ca_size_t n);
|
|
|
45
45
|
void ca_sort_quick_f32 (float32_t *a, ca_size_t n);
|
|
46
46
|
void ca_sort_quick_f64 (double *a, ca_size_t n);
|
|
47
47
|
|
|
48
|
-
/* P.1 / P.2: mergesort over 10 numeric
|
|
49
|
-
scratch buffer of the same length /
|
|
48
|
+
/* P.1 / P.2: mergesort over 10 numeric data types (`aux` is caller-supplied
|
|
49
|
+
scratch buffer of the same length / data type as `a`). */
|
|
50
50
|
void ca_sort_merge_i8 (int8_t *a, int8_t *aux, ca_size_t n);
|
|
51
51
|
void ca_sort_merge_u8 (uint8_t *a, uint8_t *aux, ca_size_t n);
|
|
52
52
|
void ca_sort_merge_i16 (int16_t *a, int16_t *aux, ca_size_t n);
|
|
@@ -58,7 +58,7 @@ void ca_sort_merge_u64 (uint64_t *a, uint64_t *aux, ca_size_t n);
|
|
|
58
58
|
void ca_sort_merge_f32 (float32_t *a, float32_t *aux, ca_size_t n);
|
|
59
59
|
void ca_sort_merge_f64 (double *a, double *aux, ca_size_t n);
|
|
60
60
|
|
|
61
|
-
/* P.3: NaN pre-partition for float
|
|
61
|
+
/* P.3: NaN pre-partition for float data types (Hoare 1-pass, returns finite count). */
|
|
62
62
|
ca_size_t ca_partition_nan_f32 (float32_t *a, ca_size_t n);
|
|
63
63
|
ca_size_t ca_partition_nan_f64 (double *a, ca_size_t n);
|
|
64
64
|
|