carray 3.0.1 → 3.0.3

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.
Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
data/ext/ca_obj_moncmp.c CHANGED
@@ -189,24 +189,6 @@ ca_moncmp_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
189
189
  ca_moncmp_func_xfer_stride(ca, starts, counts, strides, data, CA_XFER_GET);
190
190
  }
191
191
 
192
- static void
193
- ca_moncmp_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
194
- void *data, int dir)
195
- {
196
- CAMonCmp *ca = (CAMonCmp *) ap;
197
- ca_size_t i;
198
- char *out = (char *) data;
199
-
200
- if ( dir != CA_XFER_GET ) {
201
- rb_raise(rb_eRuntimeError, "CAMonCmp is read-only (xfer_addrs PUT)");
202
- }
203
- for ( i = 0; i < n; i++ ) {
204
- ca_size_t idx[CA_RANK_MAX];
205
- ca_addr2index((CArray *)ca, addrs[i], idx);
206
- ca_moncmp_func_xfer_index(ca, idx, out + i, CA_XFER_GET);
207
- }
208
- }
209
-
210
192
  ca_size_t ca_moncmp_scratch_acquire_count = 0;
211
193
  ca_size_t ca_moncmp_materialise_call_count = 0;
212
194
  ca_size_t ca_moncmp_leaf_inplace_count = 0;
@@ -242,55 +224,30 @@ ca_moncmp_try_leaf_inplace (CArray *p,
242
224
  return 1;
243
225
  }
244
226
 
227
+ /* Evaluate the cells a request names into `data` (packed, n cells). */
245
228
  static void
246
- ca_moncmp_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
247
- ca_size_t *strides, void *data, int dir)
229
+ ca_moncmp_eval (CAMonCmp *mc, const ca_lazy_req_t *req, void *data)
248
230
  {
249
- CAMonCmp *mc = (CAMonCmp *) ap;
250
- ca_size_t slab_n;
251
- int8_t k;
231
+ ca_size_t slab_n = req->n;
252
232
  void *scratch;
253
233
  ca_size_t operand_bytes;
254
- ca_size_t operand_strides[CA_RANK_MAX];
255
234
  int is_contig;
256
235
 
257
- if ( dir != CA_XFER_GET ) {
258
- rb_raise(rb_eRuntimeError, "CAMonCmp is read-only (xfer_stride PUT)");
259
- }
260
236
  ca_moncmp_materialise_call_count++;
261
237
 
262
- slab_n = 1;
263
- for ( k = 0; k < mc->ndim; k++ ) slab_n *= counts[k];
264
-
265
238
  operand_bytes = mc->parent->bytes;
266
239
 
267
- /* The operand's cell is operand_bytes wide where this view's is one byte
268
- (CA_BOOLEAN), so the caller's strides have to be restated at the
269
- operand's cell size before the pull; strides[k] / bytes is the index
270
- step, which is what the two spaces share.
271
-
272
- is_contig says the caller asked for the row-major slab that xfer_all
273
- and attach send. Only then does the parent's own buffer hold the
274
- wanted cells in the packed order the kernel reads them in, so it is
275
- also the only case the leaf in-place path is good for. */
276
- {
277
- ca_size_t native = mc->bytes;
278
- is_contig = 1;
279
- for ( k = mc->ndim - 1; k >= 0; k-- ) {
280
- operand_strides[k] = strides[k] / mc->bytes * operand_bytes;
281
- if ( strides[k] != native ) {
282
- is_contig = 0;
283
- }
284
- native *= mc->dim[k];
285
- }
286
- }
240
+ /* The leaf in-place path reads the parent's own buffer, which holds the
241
+ wanted cells in the order the kernel reads them only for the row-major
242
+ slab that xfer_all and attach send. */
243
+ is_contig = ca_lazy_req_is_packed(req, mc);
287
244
 
288
245
  /* === 1. pull parent (leaf-opt or scratch) === */
289
246
  int scratch_is_inplace = 0;
290
247
  {
291
248
  char *inplace = NULL;
292
249
  if ( is_contig &&
293
- ca_moncmp_try_leaf_inplace(mc->parent, starts, counts,
250
+ ca_moncmp_try_leaf_inplace(mc->parent, req->starts, req->counts,
294
251
  operand_bytes, &inplace) ) {
295
252
  scratch = inplace;
296
253
  scratch_is_inplace = 1;
@@ -301,8 +258,7 @@ ca_moncmp_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
301
258
  ? ca_lazy_arena_acquire_object(slab_n)
302
259
  : ca_lazy_arena_acquire(slab_n * operand_bytes);
303
260
  ca_moncmp_scratch_acquire_count++;
304
- ca_xfer_stride(mc->parent, starts, counts, operand_strides, scratch,
305
- CA_XFER_GET);
261
+ ca_lazy_req_pull(mc->parent, req, scratch, CA_XFER_GET);
306
262
  }
307
263
  }
308
264
 
@@ -312,10 +268,12 @@ ca_moncmp_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
312
268
  * fn(n, m, ptr1, i1, ptr2, i2)
313
269
  * where ptr2 is boolean8_t*
314
270
  *
315
- * Non-trapping: m=NULL (= SIMD fast path). Per-data_type kernel handles
316
- * integer is_nan etc. internally.
271
+ * m=NULL (= SIMD fast path) except for an object parent, whose cells
272
+ * call Ruby: those skip the masked cells, as the eager method does.
273
+ * Per-data_type kernel handles integer is_nan etc. internally.
317
274
  */
318
275
  {
276
+ boolean8_t *slab_mask = NULL;
319
277
  ca_moncmp_func_t fn = ca_moncmp_kernel_lookup(mc->op_id,
320
278
  mc->parent->data_type);
321
279
  if ( fn == NULL ) {
@@ -324,14 +282,44 @@ ca_moncmp_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
324
282
  "CAMonCmp: kernel not implemented (op_id=%u data_type=%d)",
325
283
  (unsigned) mc->op_id, (int) mc->parent->data_type);
326
284
  }
327
- fn(slab_n, NULL,
285
+ if ( mc->parent->data_type == CA_OBJECT ) {
286
+ slab_mask = ca_lazy_req_mask(req, 1, &mc->parent);
287
+ }
288
+ fn(slab_n, slab_mask,
328
289
  (char *) scratch, 1,
329
290
  (boolean8_t *) data, 1);
291
+ if ( slab_mask ) ca_lazy_arena_release(slab_mask);
330
292
  }
331
293
 
332
294
  if ( ! scratch_is_inplace ) ca_lazy_arena_release(scratch);
333
295
  }
334
296
 
297
+ static void
298
+ ca_moncmp_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
299
+ ca_size_t *strides, void *data, int dir)
300
+ {
301
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
302
+ ca_lazy_req_t req;
303
+ if ( dir != CA_XFER_GET ) {
304
+ rb_raise(rb_eRuntimeError, "CAMonCmp is read-only (xfer_stride PUT)");
305
+ }
306
+ ca_lazy_req_region(&req, ap, starts, counts, strides);
307
+ ca_moncmp_eval((CAMonCmp *) ap, &req, data);
308
+ }
309
+
310
+ /* An address list is evaluated in one pass, as a region is. */
311
+ static void
312
+ ca_moncmp_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
313
+ void *data, int dir)
314
+ {
315
+ ca_lazy_req_t req;
316
+ if ( dir != CA_XFER_GET ) {
317
+ rb_raise(rb_eRuntimeError, "CAMonCmp is read-only (xfer_addrs PUT)");
318
+ }
319
+ ca_lazy_req_addrs(&req, n, addrs);
320
+ ca_moncmp_eval((CAMonCmp *) ap, &req, data);
321
+ }
322
+
335
323
  static void
336
324
  ca_moncmp_func_xfer_all (void *ap, void *data, int dir)
337
325
  {
@@ -394,17 +382,16 @@ ca_moncmp_func_fill_data (void *ap, void *ptr)
394
382
  rb_raise(rb_eRuntimeError, "CAMonCmp is read-only (fill_data)");
395
383
  }
396
384
 
385
+ /* The parent's mask cell for cell, read-only as the comparison is. */
397
386
  static void
398
387
  ca_moncmp_func_create_mask (void *ap)
399
388
  {
400
389
  CAMonCmp *mc = (CAMonCmp *) ap;
401
390
  CArray *p = mc->parent;
402
391
  if ( ! ca_has_mask(p) ) return;
403
-
404
- mc->mask = (CArray *) carray_new(CA_BOOLEAN, mc->ndim, mc->dim, 0, NULL);
405
- ca_attach(p->mask);
406
- memcpy(mc->mask->ptr, p->mask->ptr, mc->elements);
407
- ca_detach(p->mask);
392
+ mc->mask = (CArray *) ca_refer_new(p->mask, CA_BOOLEAN,
393
+ mc->ndim, mc->dim, 0, 0);
394
+ ca_set_flag(mc->mask, CA_FLAG_READ_ONLY);
408
395
  }
409
396
 
410
397
  ca_operation_function_t ca_moncmp_func = {
@@ -470,6 +457,17 @@ rb_ca_moncmp_s_build (VALUE klass, VALUE p_cary, VALUE op_id_val)
470
457
  return rb_ca_moncmp_build(p_cary, op_id);
471
458
  }
472
459
 
460
+ /* Whether this node's kernel skips the cells its operands mask, rather
461
+ than computing them: object cells, which call Ruby. CArray::Fusion
462
+ reads it so a plan carries the same rule. */
463
+ static VALUE
464
+ rb_ca_moncmp_trapping (VALUE self)
465
+ {
466
+ CAMonCmp *mc;
467
+ TypedData_Get_Struct(self, CAMonCmp, &camoncmp_data_type, mc);
468
+ return ( mc->parent->data_type == CA_OBJECT ) ? Qtrue : Qfalse;
469
+ }
470
+
473
471
  static VALUE
474
472
  rb_ca_moncmp_op_id (VALUE self)
475
473
  {
@@ -535,6 +533,7 @@ rb_ca_moncmp_initialize_copy (VALUE self, VALUE other)
535
533
  {
536
534
  CAMonCmp *ca, *cs;
537
535
  TypedData_Get_Struct(self, CAMonCmp, &camoncmp_data_type, ca);
536
+ ca_check_uninitialized(ca);
538
537
  TypedData_Get_Struct(other, CAMonCmp, &camoncmp_data_type, cs);
539
538
  if ( ca_func[CA_OBJ_MONCMP].pool_init ) {
540
539
  ca_array_pool_alloc(ca, CA_OBJ_MONCMP, cs->parent->ndim);
@@ -572,6 +571,7 @@ Init_ca_obj_moncmp (void)
572
571
  rb_ca_moncmp_s_build, 2);
573
572
 
574
573
  rb_define_method(rb_cCAMonCmp, "__op_id__", rb_ca_moncmp_op_id, 0);
574
+ rb_define_method(rb_cCAMonCmp, "__trapping__", rb_ca_moncmp_trapping, 0);
575
575
 
576
576
  rb_define_singleton_method(rb_cCAMonCmp, "__reset_scratch_counter__",
577
577
  rb_ca_moncmp_s_reset_scratch_counter, 0);
data/ext/ca_obj_monop.c CHANGED
@@ -33,6 +33,7 @@
33
33
  #include "carray.h"
34
34
  #include "carray_internal.h" /* ca_lazy_arena_*, ca_is_lazy_view */
35
35
  #include "ca_monop_dispatch.h"
36
+ #include "carray_math_kernel.h" /* ca_monop_not_implement */
36
37
 
37
38
  /* ca_cast_block (carray_cast.c) */
38
39
  extern void ca_cast_block (ca_size_t n, void *ap1, void *ptr1,
@@ -284,11 +285,10 @@ ca_monop_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
284
285
  rb_raise(rb_eRuntimeError, "CAMonOp is read-only (xfer_index PUT)");
285
286
  }
286
287
  {
287
- char scratch[32]; /* max sizeof for any built-in numeric data_type */
288
+ volatile VALUE holder;
288
289
  ca_size_t bytes_per_cell = (ca_monop_is_cast(ca->op_id))
289
290
  ? ca->parent->bytes : ca->bytes;
290
- char *p = (bytes_per_cell <= 32) ? scratch
291
- : (char *) ALLOC_N(char, bytes_per_cell);
291
+ char *p = ALLOCV_N(char, holder, bytes_per_cell);
292
292
  if ( ca_monop_is_cast(ca->op_id) ) {
293
293
  /* Reverse-cast view-data_type `data` to parent data_type scratch, then
294
294
  store into parent at idx[]. */
@@ -301,7 +301,7 @@ ca_monop_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
301
301
  ca_byte_swap_buffer(ca->data_type, ca->bytes, 1, p);
302
302
  }
303
303
  ca_store_index(ca->parent, idx, p);
304
- if ( p != scratch ) xfree(p);
304
+ ALLOCV_END(holder);
305
305
  }
306
306
  return;
307
307
  }
@@ -316,38 +316,6 @@ ca_monop_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
316
316
  ca_monop_func_xfer_stride(ca, starts, counts, strides, data, CA_XFER_GET);
317
317
  }
318
318
 
319
- static void
320
- ca_monop_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
321
- void *data, int dir)
322
- {
323
- CAMonOp *ca = (CAMonOp *) ap;
324
- ca_size_t i;
325
- char *out = (char *) data;
326
-
327
- if ( dir != CA_XFER_GET ) {
328
- /* PUT: only writable-view op_ids accept writes. */
329
- if ( ! ca_monop_is_writable_view(ca->op_id) ) {
330
- rb_raise(rb_eRuntimeError, "CAMonOp is read-only (xfer_addrs PUT)");
331
- }
332
- /* Per-cell loop: forward to xfer_index PUT (handles cast/byte_swap
333
- branch internally). */
334
- for ( i = 0; i < n; i++ ) {
335
- ca_size_t idx[CA_RANK_MAX];
336
- ca_addr2index((CArray *)ca, addrs[i], idx);
337
- ca_monop_func_xfer_index(ca, idx, out + i * ca->bytes, CA_XFER_PUT);
338
- }
339
- return;
340
- }
341
-
342
- /* Naive per-addr loop: convert addr → idx, then xfer_index. This path
343
- is not optimised. */
344
- for ( i = 0; i < n; i++ ) {
345
- ca_size_t idx[CA_RANK_MAX];
346
- ca_addr2index((CArray *)ca, addrs[i], idx);
347
- ca_monop_func_xfer_index(ca, idx, out + i * ca->bytes, CA_XFER_GET);
348
- }
349
- }
350
-
351
319
  /* ------------------------------------------------------------------- */
352
320
  /* In-place chain eval (scratch = 0) */
353
321
  /* ------------------------------------------------------------------- */
@@ -369,27 +337,30 @@ ca_size_t ca_monop_materialise_call_count = 0;
369
337
  #define CA_MAX_LAZY_DEPTH 256
370
338
  #endif
371
339
 
340
+ /* Whether an op can raise on a cell, so that a masked cell must be
341
+ skipped rather than computed: any op on an object cell (it calls Ruby),
342
+ and an integer reciprocal (zero raises). */
343
+ static int
344
+ ca_monop_is_trapping (uint16_t op_id, int8_t dt)
345
+ {
346
+ if ( dt == CA_OBJECT ) {
347
+ return 1;
348
+ }
349
+ return ( op_id == CA_MONOP_RCP && dt >= CA_INT8 && dt <= CA_UINT64 );
350
+ }
351
+
372
352
  /* Apply a single op in-place on `data` (src == dst, unit element stride).
373
353
  Raises if the kernel is not implemented for the given input data_type.
374
354
 
375
- Unmask fast path:
376
- We always pass `mask == NULL` to the kernel, which selects the SIMD-
377
- friendly "no mask" branch in the existing eager kernels (see e.g.
378
- ca_monop_sqrt_float64_t in carray_math.c — `if (m) { ... } else
379
- { ...SIMD loop... }`). Mask cells contain garbage after evaluation,
380
- but they are unobservable: the output mask is built separately via
381
- the attach lifecycle (ca_monop_func_create_mask + parent.mask
382
- CARefer), so reads of masked cells return UNDEF regardless of byte
383
- contents. The mask marks cells as undefined; it does not guard their
384
- bytes, so writing garbage into a masked cell is licensed.
385
-
386
- A "partial mask slow path" (= a cell-wise branch to skip masked-cell
387
- compute) is a possible future micro-optimisation; it is not done
388
- because masked-cell compute does not affect correctness and the SIMD
389
- path is faster overall for typical mask densities. */
355
+ `mask` is NULL except for an op that can raise on a cell: the kernel
356
+ then takes the SIMD-friendly "no mask" branch and computes the masked
357
+ cells too. Their bytes are unobservable -- the output mask is the
358
+ parent's (ca_monop_func_create_mask) -- and the mask marks cells as
359
+ undefined rather than guarding their bytes, so writing anything into a
360
+ masked cell is licensed. */
390
361
  static void
391
362
  apply_monop_in_place (uint16_t op_id, int8_t cur_dt,
392
- ca_size_t slab_n, void *data)
363
+ ca_size_t slab_n, void *data, boolean8_t *mask)
393
364
  {
394
365
  ca_monop_func_t fn = ca_monop_kernel_lookup(op_id, cur_dt);
395
366
  if ( fn == NULL ) {
@@ -397,7 +368,7 @@ apply_monop_in_place (uint16_t op_id, int8_t cur_dt,
397
368
  "CAMonOp: kernel not implemented (op_id=%u data_type=%d)",
398
369
  (unsigned) op_id, (int) cur_dt);
399
370
  }
400
- fn(slab_n, NULL, (char *) data, 1, (char *) data, 1);
371
+ fn(slab_n, mask, (char *) data, 1, (char *) data, 1);
401
372
  }
402
373
 
403
374
  /* Minimal CArray-shaped stub used as ca_cast_block's data_type-only carrier.
@@ -434,38 +405,25 @@ typedef struct {
434
405
  static int8_t
435
406
  pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
436
407
  int has_cast_at_leaf, int8_t out_bytes,
437
- ca_size_t slab_n,
438
- ca_size_t *starts, ca_size_t *counts,
439
- ca_size_t *strides, void *data)
408
+ const ca_lazy_req_t *req, void *data)
440
409
  {
441
410
  ca_size_t leaf_bytes = leaf->bytes;
411
+ ca_size_t slab_n = req->n;
442
412
 
443
413
  /* Path 1: no size mismatch, no cast — direct pull. */
444
414
  if ( leaf_bytes == out_bytes && ! has_cast_at_leaf ) {
445
- ca_xfer_stride(leaf, starts, counts, strides, data, CA_XFER_GET);
415
+ ca_lazy_req_pull(leaf, req, data, CA_XFER_GET);
446
416
  return leaf->data_type;
447
417
  }
448
418
 
449
- /* Paths 2 & 3 both need a leaf-sized scratch + leaf-byte strides
450
- for the leaf pull. */
451
- ca_size_t parent_strides[CA_RANK_MAX];
419
+ /* Paths 2 & 3 both pull the leaf into a leaf-sized scratch. */
452
420
  ca_size_t total_bytes = slab_n * leaf_bytes;
453
421
  void *scratch;
454
- volatile VALUE holder;
455
- int8_t k;
456
- {
457
- ca_size_t s = leaf_bytes;
458
- for ( k = leaf->ndim - 1; k >= 0; k-- ) {
459
- parent_strides[k] = s;
460
- s *= counts[k];
461
- }
462
- }
463
- (void) holder;
464
422
  scratch = ( leaf->data_type == CA_OBJECT )
465
423
  ? ca_lazy_arena_acquire_object(slab_n)
466
424
  : ca_lazy_arena_acquire(total_bytes);
467
425
  ca_monop_scratch_acquire_count++;
468
- ca_xfer_stride(leaf, starts, counts, parent_strides, scratch, CA_XFER_GET);
426
+ ca_lazy_req_pull(leaf, req, scratch, CA_XFER_GET);
469
427
 
470
428
  int8_t result_dt;
471
429
  if ( has_cast_at_leaf ) {
@@ -486,20 +444,21 @@ pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
486
444
  return result_dt;
487
445
  }
488
446
 
447
+ /* Evaluate (GET) or write (PUT, writable views only) the cells a request
448
+ names; `data` holds them packed. */
489
449
  static void
490
- ca_monop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
491
- ca_size_t *strides, void *data, int dir)
450
+ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
492
451
  {
493
- CAMonOp *mo = (CAMonOp *) ap;
494
452
  uint16_t op_chain[CA_MAX_LAZY_DEPTH];
495
453
  int chain_len = 0;
496
454
  CArray *leaf;
497
- ca_size_t slab_n;
498
- int8_t k;
455
+ ca_size_t slab_n = req->n;
499
456
  int8_t cur_dt;
500
457
  int i;
501
458
  int has_cast_at_leaf = 0;
502
459
  int chain_apply_count;
460
+ boolean8_t *slab_mask = NULL;
461
+ int mask_taken = 0;
503
462
 
504
463
  /* Writable-view op_ids (cast / byte_swap) use CAFake-style xfer_stride
505
464
  for both GET and PUT. This avoids the chain-materialise inline path
@@ -510,21 +469,13 @@ ca_monop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
510
469
  - byte_swap: same data_type → pstrides = strides; involution apply. */
511
470
  if ( ca_monop_is_writable_view(mo->op_id) ) {
512
471
  int is_cast = ca_monop_is_cast(mo->op_id);
513
- ca_size_t pstrides[CA_RANK_MAX];
514
- ca_size_t n = 1;
472
+ ca_size_t n = slab_n;
515
473
  ca_size_t parent_bytes = is_cast ? mo->parent->bytes : mo->bytes;
516
- int8_t kk;
517
474
  char *v;
518
475
  volatile VALUE holder;
519
- for ( kk = 0; kk < mo->ndim; kk++ ) {
520
- n *= counts[kk];
521
- pstrides[kk] = is_cast
522
- ? (strides[kk] / mo->bytes * mo->parent->bytes)
523
- : strides[kk];
524
- }
525
476
  v = ALLOCV_N(char, holder, n * parent_bytes);
526
477
  if ( dir == CA_XFER_GET ) {
527
- ca_xfer_stride(mo->parent, starts, counts, pstrides, v, CA_XFER_GET);
478
+ ca_lazy_req_pull(mo->parent, req, v, CA_XFER_GET);
528
479
  if ( is_cast ) {
529
480
  ca_cast_block(n, mo->parent, v, (CArray *) mo, data);
530
481
  }
@@ -543,7 +494,7 @@ ca_monop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
543
494
  memcpy(v, data, n * mo->bytes);
544
495
  ca_byte_swap_buffer(mo->data_type, mo->bytes, n, v);
545
496
  }
546
- ca_xfer_stride(mo->parent, starts, counts, pstrides, v, CA_XFER_PUT);
497
+ ca_lazy_req_pull(mo->parent, req, v, CA_XFER_PUT);
547
498
  }
548
499
  ALLOCV_END(holder);
549
500
  return;
@@ -564,8 +515,8 @@ ca_monop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
564
515
  while ( leaf->obj_type == CA_OBJ_MONOP ) {
565
516
  CAMonOp *node = (CAMonOp *) leaf;
566
517
  if ( chain_len >= CA_MAX_LAZY_DEPTH ) {
567
- rb_raise(rb_eRuntimeError,
568
- "CAMonOp chain depth exceeds CA_MAX_LAZY_DEPTH (%d)",
518
+ rb_raise(rb_eArgError,
519
+ "lazy expression too deep: more than %d operations in a chain",
569
520
  CA_MAX_LAZY_DEPTH);
570
521
  }
571
522
  op_chain[chain_len++] = node->op_id;
@@ -586,16 +537,12 @@ ca_monop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
586
537
  }
587
538
 
588
539
  /* === 2. pull leaf into output buffer (with optional cast) === */
589
- slab_n = 1;
590
- for ( k = 0; k < mo->ndim; k++ ) {
591
- slab_n *= counts[k];
592
- }
593
540
  cur_dt = pull_leaf_with_optional_cast(
594
541
  leaf,
595
542
  has_cast_at_leaf ? op_chain[chain_len - 1] : 0,
596
543
  has_cast_at_leaf,
597
544
  (int8_t) mo->bytes,
598
- slab_n, starts, counts, strides, data
545
+ req, data
599
546
  );
600
547
 
601
548
  /* === 3. apply remaining chain in reverse (leaf side → outermost) ===
@@ -626,10 +573,38 @@ ca_monop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
626
573
  ca_lazy_arena_release(scratch);
627
574
  cur_dt = target_dt;
628
575
  } else {
629
- apply_monop_in_place(op_id, cur_dt, slab_n, data);
576
+ if ( ! mask_taken && ca_monop_is_trapping(op_id, cur_dt) ) {
577
+ slab_mask = ca_lazy_req_mask(req, 1, &leaf);
578
+ mask_taken = 1;
579
+ }
580
+ apply_monop_in_place(op_id, cur_dt, slab_n, data,
581
+ ca_monop_is_trapping(op_id, cur_dt) ? slab_mask : NULL);
630
582
  cur_dt = ca_lazy_promote_monop(op_id, cur_dt);
631
583
  }
632
584
  }
585
+ if ( slab_mask ) {
586
+ ca_lazy_arena_release(slab_mask);
587
+ }
588
+ }
589
+
590
+ static void
591
+ ca_monop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
592
+ ca_size_t *strides, void *data, int dir)
593
+ {
594
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
595
+ ca_lazy_req_t req;
596
+ ca_lazy_req_region(&req, ap, starts, counts, strides);
597
+ ca_monop_eval((CAMonOp *) ap, &req, data, dir);
598
+ }
599
+
600
+ /* An address list is evaluated in one pass, as a region is. */
601
+ static void
602
+ ca_monop_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
603
+ void *data, int dir)
604
+ {
605
+ ca_lazy_req_t req;
606
+ ca_lazy_req_addrs(&req, n, addrs);
607
+ ca_monop_eval((CAMonOp *) ap, &req, data, dir);
633
608
  }
634
609
 
635
610
  static void
@@ -774,10 +749,12 @@ ca_monop_func_sync (void *ap)
774
749
  }
775
750
  }
776
751
  else {
777
- /* byte_swap (involution): apply on ca.ptr in place (re-swap), then
778
- memcpy ca.ptr → parent.ptr. Mirrors CAByteSwap sync. */
779
- ca_byte_swap_buffer(ca->data_type, ca->bytes, ca->elements, ca->ptr);
752
+ /* byte_swap (involution): swap a copy back to the parent's byte order.
753
+ Not ca.ptr in place -- the window stays open after a sync, and a
754
+ second sync (a stack holding this view twice) would swap it again. */
780
755
  memcpy(ca->parent->ptr, ca->ptr, ca_length(ca));
756
+ ca_byte_swap_buffer(ca->data_type, ca->bytes, ca->elements,
757
+ ca->parent->ptr);
781
758
  }
782
759
  ca_sync(ca->parent);
783
760
  }
@@ -810,9 +787,8 @@ ca_monop_func_fill_data (void *ap, void *ptr)
810
787
  {
811
788
  int is_cast = ca_monop_is_cast(ca->op_id);
812
789
  ca_size_t parent_bytes = is_cast ? ca->parent->bytes : ca->bytes;
813
- char stack_v[32];
814
- char *v = (parent_bytes <= 32) ? stack_v
815
- : (char *) xmalloc(parent_bytes);
790
+ volatile VALUE holder;
791
+ char *v = ALLOCV_N(char, holder, parent_bytes);
816
792
  if ( is_cast ) {
817
793
  ca_ptr2ptr((CArray *) ca, ptr, ca->parent, v);
818
794
  }
@@ -821,7 +797,7 @@ ca_monop_func_fill_data (void *ap, void *ptr)
821
797
  ca_byte_swap_buffer(ca->data_type, ca->bytes, 1, v);
822
798
  }
823
799
  ca_fill(ca->parent, v);
824
- if ( v != stack_v ) xfree(v);
800
+ ALLOCV_END(holder);
825
801
  }
826
802
  }
827
803
 
@@ -829,12 +805,12 @@ static void
829
805
  ca_monop_func_create_mask (void *ap)
830
806
  {
831
807
  CAMonOp *ca = (CAMonOp *) ap;
832
- if ( ! ca->parent->mask ) {
833
- ca_create_mask(ca->parent);
808
+ ca_view_func_create_mask(ca);
809
+ /* A cast view writes through to its parent, mask included; any other
810
+ operation is read-only and so is its mask. */
811
+ if ( ca_test_flag(ca, CA_FLAG_READ_ONLY) ) {
812
+ ca_set_flag(ca->mask, CA_FLAG_READ_ONLY);
834
813
  }
835
- ca->mask =
836
- (CArray *) ca_refer_new(ca->parent->mask,
837
- CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
838
814
  }
839
815
 
840
816
  ca_operation_function_t ca_monop_func = {
@@ -933,8 +909,35 @@ rb_ca_monop_build (VALUE cary, uint16_t op_id)
933
909
  }
934
910
 
935
911
  parent_dt = parent->data_type;
912
+
913
+ /* A boolean parent, as rb_ca_call_monop and the data_type-changing
914
+ monops treat it: a math function wants an explicit cast, and an op
915
+ with no boolean kernel reads the 0/1 storage in int64. */
916
+ if ( parent_dt == CA_BOOLEAN && ! ca_monop_is_writable_view(op_id) ) {
917
+ if ( ca_monop_is_widening(op_id) ) {
918
+ rb_raise(rb_eCADataTypeError,
919
+ "invalid data type for monop (not implemented)");
920
+ }
921
+ if ( ca_monop_kernel_lookup(op_id, CA_BOOLEAN) == ca_monop_not_implement ) {
922
+ target = rb_ca_monop_new(target, CA_MONOP_CAST_BASE + CA_INT64);
923
+ TypedData_Get_Struct(target, CArray, &carray_data_type, parent);
924
+ parent_dt = CA_INT64;
925
+ }
926
+ }
927
+
936
928
  kernel_in_dt = ca_monop_kernel_input_data_type(op_id, parent_dt);
937
929
 
930
+ /* No kernel for the parent, nor one a cast could reach (a fixlen cell
931
+ converts to nothing numeric): raise as the eager monop does, before
932
+ a view is built over it. */
933
+ if ( ! ca_monop_is_writable_view(op_id) &&
934
+ ( ca_monop_kernel_lookup(op_id, kernel_in_dt) == ca_monop_not_implement ||
935
+ ( parent_dt == CA_FIXLEN &&
936
+ ca_monop_kernel_lookup(op_id, CA_FIXLEN) == ca_monop_not_implement ) ) ) {
937
+ rb_raise(rb_eCADataTypeError,
938
+ "invalid data type for monop (not implemented)");
939
+ }
940
+
938
941
  /* Insert cast node if the kernel wants a different input data_type. */
939
942
  if ( kernel_in_dt != parent_dt ) {
940
943
  uint16_t cast_op_id = CA_MONOP_CAST_BASE + (uint16_t) kernel_in_dt;
@@ -964,6 +967,19 @@ rb_ca_is_lazy_view_p (VALUE self)
964
967
  }
965
968
 
966
969
  /* Read CAMonOp's op_id for inspect/dump_tree consumers in Ruby. */
970
+ /* Whether this node's kernel skips the cells its operands mask, rather
971
+ than computing them: a kernel that can raise on a cell or calls Ruby
972
+ for it. CArray::Fusion reads it so a plan carries the same rule. */
973
+ static VALUE
974
+ rb_ca_monop_trapping (VALUE self)
975
+ {
976
+ CAMonOp *mo;
977
+ TypedData_Get_Struct(self, CAMonOp, &camonop_data_type, mo);
978
+ return ( ! ca_monop_is_cast(mo->op_id)
979
+ && ca_monop_is_trapping(mo->op_id, mo->parent->data_type) )
980
+ ? Qtrue : Qfalse;
981
+ }
982
+
967
983
  static VALUE
968
984
  rb_ca_monop_op_id (VALUE self)
969
985
  {
@@ -1018,6 +1034,7 @@ rb_ca_monop_initialize_copy (VALUE self, VALUE other)
1018
1034
  {
1019
1035
  CAMonOp *ca, *cs;
1020
1036
  TypedData_Get_Struct(self, CAMonOp, &camonop_data_type, ca);
1037
+ ca_check_uninitialized(ca);
1021
1038
  TypedData_Get_Struct(other, CAMonOp, &camonop_data_type, cs);
1022
1039
  if ( ca_func[CA_OBJ_MONOP].pool_init ) {
1023
1040
  ca_array_pool_alloc(ca, CA_OBJ_MONOP, cs->parent->ndim);
@@ -1110,6 +1127,7 @@ Init_ca_obj_monop (void)
1110
1127
  /* op_id accessor for inspect / dump_tree consumers in Ruby. */
1111
1128
  rb_define_method(rb_cCAMonOp, "__op_id__",
1112
1129
  rb_ca_monop_op_id, 0);
1130
+ rb_define_method(rb_cCAMonOp, "__trapping__", rb_ca_monop_trapping, 0);
1113
1131
 
1114
1132
  /* Test instrumentation (not user-facing API). */
1115
1133
  rb_define_singleton_method(rb_cCAMonOp, "__reset_scratch_counter__",