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.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/CHANGELOG.md +325 -3
  4. data/{NEWS.md → CHANGELOG.v1.md} +1 -1
  5. data/README.md +32 -32
  6. data/carray.gemspec +12 -8
  7. data/ext/ca_axis_group.c +55 -19
  8. data/ext/ca_binop_dispatch.c +5 -6
  9. data/ext/ca_binop_dispatch.h +0 -7
  10. data/ext/ca_categorical_iterator.c +13 -13
  11. data/ext/ca_for_each_element.h +6 -8
  12. data/ext/ca_group_iter.c +1 -1
  13. data/ext/ca_kernel_iterator.c +69 -23
  14. data/ext/ca_kernel_iterator.h +3 -10
  15. data/ext/ca_obj_bincmp.c +57 -27
  16. data/ext/ca_obj_binop.c +86 -30
  17. data/ext/ca_obj_const_string.c +3 -1
  18. data/ext/ca_obj_face.c +80 -0
  19. data/ext/ca_obj_face.h +33 -1
  20. data/ext/ca_obj_fake.c +11 -0
  21. data/ext/ca_obj_fixlen_string.c +3 -1
  22. data/ext/ca_obj_grid.c +11 -3
  23. data/ext/ca_obj_meld.c +13 -8
  24. data/ext/ca_obj_moncmp.c +33 -14
  25. data/ext/ca_obj_monop.c +19 -7
  26. data/ext/ca_obj_object.c +99 -4
  27. data/ext/ca_obj_record.c +3 -1
  28. data/ext/ca_obj_refer.c +30 -6
  29. data/ext/ca_obj_roll.c +13 -5
  30. data/ext/ca_obj_select_axis.c +16 -6
  31. data/ext/ca_obj_shift.c +3 -3
  32. data/ext/ca_obj_stride.c +106 -23
  33. data/ext/ca_obj_string.c +3 -1
  34. data/ext/ca_obj_tile.c +13 -5
  35. data/ext/ca_obj_time.c +3 -1
  36. data/ext/ca_obj_timedelta.c +3 -1
  37. data/ext/ca_obj_transpose.c +1 -1
  38. data/ext/ca_obj_triop.c +62 -23
  39. data/ext/ca_obj_window.c +86 -17
  40. data/ext/ca_op_cmplx64.h +123 -0
  41. data/ext/ca_op_ipower.c +0 -3
  42. data/ext/ca_sort_kernels.h +5 -5
  43. data/ext/ca_sweep_engine.c +78 -35
  44. data/ext/ca_sweep_engine.h +13 -4
  45. data/ext/ca_transform_common.c +7 -0
  46. data/ext/carray.h +127 -130
  47. data/ext/carray_access.c +56 -34
  48. data/ext/carray_bincount.c +8 -8
  49. data/ext/carray_broadcast.c +99 -6
  50. data/ext/carray_build_flags.h +3 -0
  51. data/ext/carray_call_cfunc.c +1491 -0
  52. data/ext/carray_call_cfunc.h +153 -0
  53. data/ext/carray_cast.c +69 -31
  54. data/ext/carray_conversion.c +22 -22
  55. data/ext/carray_copy.c +12 -2
  56. data/ext/carray_core.c +57 -10
  57. data/ext/carray_count.c +1 -1
  58. data/ext/carray_element.c +7 -7
  59. data/ext/carray_factorize.c +28 -28
  60. data/ext/carray_hold.c +1 -1
  61. data/ext/carray_index_classifier.c +7 -21
  62. data/ext/carray_internal.h +19 -3
  63. data/ext/carray_kernels_bincmp.c +1 -0
  64. data/ext/carray_kernels_binop.c +320 -298
  65. data/ext/carray_kernels_init.c +1095 -0
  66. data/ext/carray_kernels_map.c +1 -0
  67. data/ext/carray_kernels_moncmp.c +1 -0
  68. data/ext/carray_kernels_monop.c +412 -411
  69. data/ext/carray_kernels_reduce_aggregate.c +88 -87
  70. data/ext/carray_kernels_reduce_boolean.c +1 -0
  71. data/ext/carray_kernels_reduce_cumulative.c +54 -53
  72. data/ext/carray_kernels_reduce_extreme.c +51 -50
  73. data/ext/carray_kernels_reduce_variance.c +1 -0
  74. data/ext/carray_kernels_scan.c +1 -0
  75. data/ext/carray_kernels_search.c +93 -92
  76. data/ext/carray_kernels_sort.c +1 -0
  77. data/ext/carray_kernels_triop.c +1 -0
  78. data/ext/carray_lazy.c +175 -5
  79. data/ext/carray_mask.c +6 -5
  80. data/ext/carray_median_percentile.c +1 -1
  81. data/ext/carray_memory_view.c +46 -21
  82. data/ext/carray_operator.c +46 -49
  83. data/ext/carray_partition.c +4 -4
  84. data/ext/carray_random.c +7 -7
  85. data/ext/carray_scatter.c +1 -1
  86. data/ext/carray_sort.c +3 -3
  87. data/ext/carray_sort_kernel.c +10 -10
  88. data/ext/carray_test.c +1 -1
  89. data/ext/extconf.rb +18 -0
  90. data/ext/mk_call_cfunc.rb +243 -52
  91. data/ext/mkkernel.rb +354 -100
  92. data/ext/ruby_carray.c +18 -16
  93. data/ext/version.h +4 -4
  94. data/lib/carray/autoload_carray.rb +19 -0
  95. data/lib/carray/autoload_method_extension.rb +2 -1
  96. data/lib/carray/axis_group.rb +7 -7
  97. data/lib/carray/basics.rb +59 -59
  98. data/lib/carray/bincount_nd.rb +12 -12
  99. data/lib/carray/block_iterator.rb +24 -14
  100. data/lib/carray/categorical.rb +4 -4
  101. data/lib/carray/categorical_iterator.rb +98 -76
  102. data/lib/carray/conditional.rb +14 -14
  103. data/lib/carray/construct.rb +21 -0
  104. data/lib/carray/core_extensions.rb +62 -5
  105. data/lib/carray/data_type_extension.rb +18 -1
  106. data/lib/carray/frame/concat.rb +3 -3
  107. data/lib/carray/frame/convert.rb +1 -1
  108. data/lib/carray/frame/frame.rb +4 -11
  109. data/lib/carray/frame/io.rb +159 -2
  110. data/lib/carray/frame/sort.rb +1 -1
  111. data/lib/carray/frame/verbs.rb +18 -1
  112. data/lib/carray/fuse_source.rb +123 -0
  113. data/lib/carray/fusion.rb +218 -0
  114. data/lib/carray/histogram.rb +16 -16
  115. data/lib/carray/inspect.rb +1 -7
  116. data/lib/carray/iterator.rb +4 -3
  117. data/lib/carray/lazy.rb +125 -73
  118. data/lib/carray/meld_reduce.rb +2 -2
  119. data/lib/carray/methods/align_addr.rb +1 -1
  120. data/lib/carray/methods/composition.rb +1 -1
  121. data/lib/carray/methods/is_in.rb +12 -12
  122. data/lib/carray/methods/locate_addr.rb +6 -1
  123. data/lib/carray/methods/mask_duplicates.rb +1 -1
  124. data/lib/carray/methods/meshgrid.rb +4 -5
  125. data/lib/carray/methods/mode.rb +2 -2
  126. data/lib/carray/methods/nunique.rb +1 -1
  127. data/lib/carray/methods/snap.rb +7 -2
  128. data/lib/carray/methods/unique.rb +3 -3
  129. data/lib/carray/methods/value_counts.rb +2 -2
  130. data/lib/carray/runtime.rb +0 -19
  131. data/lib/carray/slab_iterator.rb +20 -7
  132. data/lib/carray/string_operation_extension.rb +5 -5
  133. data/lib/carray/time.rb +890 -491
  134. data/lib/carray/window_iterator.rb +285 -13
  135. data/lib/carray.rb +5 -5
  136. data/yard-stubs/ca_obj_array.rb +385 -0
  137. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  138. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  139. data/yard-stubs/ca_obj_block.rb +73 -0
  140. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  141. data/yard-stubs/ca_obj_fake.rb +31 -0
  142. data/yard-stubs/ca_obj_farray.rb +32 -0
  143. data/yard-stubs/ca_obj_field.rb +45 -0
  144. data/yard-stubs/ca_obj_grid.rb +35 -0
  145. data/yard-stubs/ca_obj_refer.rb +72 -0
  146. data/yard-stubs/ca_obj_roll.rb +45 -0
  147. data/yard-stubs/ca_obj_shift.rb +43 -0
  148. data/yard-stubs/ca_obj_stride.rb +181 -0
  149. data/yard-stubs/ca_obj_tile.rb +29 -0
  150. data/yard-stubs/ca_obj_transpose.rb +40 -0
  151. data/yard-stubs/ca_obj_window.rb +49 -0
  152. data/yard-stubs/carray_access.rb +131 -0
  153. data/yard-stubs/carray_attribute.rb +246 -0
  154. data/yard-stubs/carray_broadcast.rb +37 -0
  155. data/yard-stubs/carray_cast.rb +489 -0
  156. data/yard-stubs/carray_class.rb +65 -0
  157. data/yard-stubs/carray_conversion.rb +76 -0
  158. data/yard-stubs/carray_copy.rb +79 -0
  159. data/yard-stubs/carray_core.rb +114 -0
  160. data/yard-stubs/carray_count.rb +79 -0
  161. data/yard-stubs/carray_element.rb +108 -0
  162. data/yard-stubs/carray_generate.rb +66 -0
  163. data/yard-stubs/carray_lazy.rb +23 -0
  164. data/yard-stubs/carray_loop.rb +140 -0
  165. data/yard-stubs/carray_mask.rb +259 -0
  166. data/yard-stubs/carray_math.rb +132 -0
  167. data/yard-stubs/carray_mathfunc.rb +45 -0
  168. data/yard-stubs/carray_median_percentile.rb +89 -0
  169. data/yard-stubs/carray_memory_view.rb +163 -0
  170. data/yard-stubs/carray_order.rb +312 -0
  171. data/yard-stubs/carray_random.rb +89 -0
  172. data/yard-stubs/carray_scatter.rb +106 -0
  173. data/yard-stubs/carray_slab.rb +57 -0
  174. data/yard-stubs/carray_sort.rb +163 -0
  175. data/yard-stubs/carray_test.rb +85 -0
  176. data/yard-stubs/carray_undef.rb +64 -0
  177. data/yard-stubs/carray_utils.rb +97 -0
  178. data/yard-stubs/ruby_carray.rb +193 -0
  179. metadata +59 -10
  180. data/ext/ca_obj_unbound_repeat.c +0 -496
@@ -179,6 +179,7 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
179
179
  st->attached[k_op] = 0;
180
180
  }
181
181
  st->m0 = NULL;
182
+ st->mask_scratch = NULL;
182
183
  st->n_kernel = 1;
183
184
  st->chunk_off = 0;
184
185
  st->chunk_n = 0;
@@ -206,7 +207,7 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
206
207
  }
207
208
 
208
209
  /* chunk-size policy: inner = donor's product of dims[1..]; chunk_n_max
209
- * = compute_n on donor->bytes (= dtype-dependent 32KB target). */
210
+ * = compute_n on donor->bytes (= type-dependent 32KB target). */
210
211
  if (shape_donor) {
211
212
  st->inner = ca_chunk_inner_size(shape_donor);
212
213
  st->chunk_n_max = ca_chunk_compute_n(st->n_kernel, st->inner,
@@ -243,8 +244,13 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
243
244
  }
244
245
  }
245
246
 
246
- /* iter mask = OR of INPUT operand masks (full size, n_kernel bytes).
247
- * Per-chunk mask gather is a future optim; this MVP gathers once. */
247
+ /* iter mask = OR of INPUT operand masks, chunk-sized (chunk_n_max bytes)
248
+ * and re-gathered per chunk in ca_sweep_next_chunk. m0 is indexed by the
249
+ * offset within the chunk, m0[k] for k < chunk_n -- NOT by the flat cell
250
+ * index. Sizing it to n_kernel instead would leave one allocation still
251
+ * scaling with the operand, which is the thing this path exists to avoid:
252
+ * at 1 byte per cell it is an eighth of an f64 operand, but an eighth of
253
+ * unbounded is still unbounded. */
248
254
  for (k_op = 0; k_op < st->n_ops; k_op++) {
249
255
  if (st->fsync[k_op] == '0' && ca_has_mask(st->cx[k_op])) {
250
256
  any_input_mask = 1;
@@ -258,31 +264,69 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
258
264
  st->src_label ? st->src_label : "ca_sweep_acquire_chunked");
259
265
  }
260
266
  if (any_input_mask) {
261
- st->m0 = (boolean8_t *) ca_lazy_arena_acquire(st->n_kernel);
262
- memset(st->m0, 0, st->n_kernel);
267
+ st->m0 = (boolean8_t *) ca_lazy_arena_acquire(st->chunk_n_max);
268
+ st->mask_scratch = (boolean8_t *) ca_lazy_arena_acquire(st->chunk_n_max);
269
+ memset(st->m0, 0, st->chunk_n_max);
270
+ /* The OUTPUT masks have to exist before the walk starts, because each
271
+ * chunk's m0 is flushed into them as the walk passes -- there is no
272
+ * whole m0 left at release time to propagate in one go. */
263
273
  for (k_op = 0; k_op < st->n_ops; k_op++) {
264
274
  CArray *ca = st->cx[k_op];
265
- if (st->fsync[k_op] != '0') continue;
275
+ if (st->fsync[k_op] != '1') continue;
266
276
  ca_update_mask(ca);
267
- if (!ca->mask) continue;
268
- if (ca_is_scalar(ca)) {
269
- boolean8_t bit = 0;
270
- ca_xfer_all(ca->mask, &bit, CA_XFER_GET);
271
- if (bit) memset(st->m0, 1, st->n_kernel);
272
- } else {
273
- boolean8_t *ms = (boolean8_t *) ca_lazy_arena_acquire(st->n_kernel);
274
- ca_size_t j;
275
- ca_xfer_all(ca->mask, ms, CA_XFER_GET);
276
- for (j = 0; j < st->n_kernel; j++) st->m0[j] |= ms[j];
277
- ca_lazy_arena_release(ms);
278
- }
277
+ if (!ca->mask) ca_create_mask(ca);
279
278
  }
280
279
  }
281
280
 
282
- /* CAREFUL: m0 -> OUTPUT mask propagation is deferred to release
283
- * time so that author per-cell m_out writes during the chunk loop
284
- * are captured in the final OUTPUT mask. Do not fold this into
285
- * acquire; doing so would clobber the author's writes. */
281
+ /* CAREFUL: m0 -> OUTPUT mask propagation happens per chunk, at the point
282
+ * the chunk is finished (= the top of the next ca_sweep_next_chunk, and
283
+ * once more in release), never at acquire. Author per-cell m_out writes
284
+ * land in m0 during the chunk loop and must be captured after that loop
285
+ * has run, not before it. */
286
+ }
287
+
288
+ /* OR one INPUT operand's mask for the current chunk into m0. Gathered via
289
+ * ca_chunked_gather rather than attached, so no operand mask is ever
290
+ * attached -- the same invariant the whole-buffer path keeps with
291
+ * ca_xfer_all. */
292
+ static void
293
+ ca_sweep_gather_chunk_mask (ca_sweep_state_t *st, ca_size_t off, ca_size_t n)
294
+ {
295
+ int k_op;
296
+ ca_size_t j;
297
+
298
+ memset(st->m0, 0, n);
299
+ for (k_op = 0; k_op < st->n_ops; k_op++) {
300
+ CArray *ca = st->cx[k_op];
301
+ if (st->fsync[k_op] != '0') continue;
302
+ ca_update_mask(ca);
303
+ if (!ca->mask) continue;
304
+ if (ca_is_scalar(ca)) {
305
+ /* one cell, broadcast across the chunk */
306
+ boolean8_t bit = 0;
307
+ ca_xfer_all(ca->mask, &bit, CA_XFER_GET);
308
+ if (bit) memset(st->m0, 1, n);
309
+ } else {
310
+ ca_chunked_gather(ca->mask, off, n, st->mask_scratch);
311
+ for (j = 0; j < n; j++) st->m0[j] |= st->mask_scratch[j];
312
+ }
313
+ }
314
+ }
315
+
316
+ /* Copy the chunk just finished out to every OUTPUT operand's mask. Runs
317
+ * after the author's loop over that chunk, so m_out writes into m0 are
318
+ * carried through. */
319
+ static void
320
+ ca_sweep_flush_chunk_mask (ca_sweep_state_t *st)
321
+ {
322
+ int k_op;
323
+ if (!st->m0 || st->chunked_state != 1 || st->chunk_n == 0) return;
324
+ for (k_op = 0; k_op < st->n_ops; k_op++) {
325
+ CArray *ca = st->cx[k_op];
326
+ if (st->fsync[k_op] != '1') continue;
327
+ if (!ca->mask) continue;
328
+ memcpy((boolean8_t *) ca->mask->ptr + st->chunk_off, st->m0, st->chunk_n);
329
+ }
286
330
  }
287
331
 
288
332
  int
@@ -296,6 +340,8 @@ ca_sweep_next_chunk (ca_sweep_state_t *st)
296
340
  st->chunk_off = 0;
297
341
  st->chunked_state = 1;
298
342
  } else {
343
+ /* the chunk that just finished is the author's last word on its mask */
344
+ ca_sweep_flush_chunk_mask(st);
299
345
  /* advance */
300
346
  st->chunk_off += st->chunk_n;
301
347
  }
@@ -326,6 +372,8 @@ ca_sweep_next_chunk (ca_sweep_state_t *st)
326
372
  }
327
373
  }
328
374
 
375
+ if (st->m0) ca_sweep_gather_chunk_mask(st, off, n);
376
+
329
377
  return 1;
330
378
  }
331
379
 
@@ -333,19 +381,10 @@ void
333
381
  ca_sweep_release_chunked (ca_sweep_state_t *st)
334
382
  {
335
383
  int k_op;
336
- /* Propagate (possibly author-mutated) m0 to OUTPUT mask before
337
- * sync. For INOUT_MASKED forms this captures the author's per-cell
338
- * m_out writes; other forms behave the same as the whole-buffer
339
- * acquire-time propagation. */
340
- if (st->m0) {
341
- for (k_op = 0; k_op < st->n_ops; k_op++) {
342
- CArray *ca = st->cx[k_op];
343
- if (st->fsync[k_op] != '1') continue;
344
- ca_update_mask(ca);
345
- if (!ca->mask) ca_create_mask(ca);
346
- memcpy(ca->mask->ptr, st->m0, st->n_kernel);
347
- }
348
- }
384
+ /* The final chunk has no next_chunk call to flush it, so it is flushed
385
+ * here. For INOUT_MASKED forms this is what captures the author's
386
+ * per-cell m_out writes over that last chunk. */
387
+ ca_sweep_flush_chunk_mask(st);
349
388
  /* sync OUTPUTs (reverse order, regardless of whether chunk loop ran) */
350
389
  for (k_op = st->n_ops - 1; k_op >= 0; k_op--) {
351
390
  if (st->fsync[k_op] == '1') ca_sync(st->cx[k_op]);
@@ -363,6 +402,10 @@ ca_sweep_release_chunked (ca_sweep_state_t *st)
363
402
  ca_lazy_arena_release(st->m0);
364
403
  st->m0 = NULL;
365
404
  }
405
+ if (st->mask_scratch) {
406
+ ca_lazy_arena_release(st->mask_scratch);
407
+ st->mask_scratch = NULL;
408
+ }
366
409
  }
367
410
 
368
411
  /* ===== WHOLE_BUFFER function form (rb_ensure-protected) ===== */
@@ -85,6 +85,8 @@ typedef struct ca_sweep_state {
85
85
  ca_size_t inner; /* product of dims except outermost (= row
86
86
  size in elements for outer-axis chunking) */
87
87
  int chunked_state; /* 0=pre-init, 1=active, 2=done */
88
+ boolean8_t *mask_scratch; /* chunk-sized staging for the per-chunk
89
+ mask OR; NULL when no INPUT is masked */
88
90
  } ca_sweep_state_t;
89
91
 
90
92
  /* Validate fsync length, acquire per-op buffers, compute broadcast shape +
@@ -134,10 +136,17 @@ void ca_sweep_check_same_shape (CArray *ca_in, CArray *ca_out,
134
136
  * for the entire walk; ca_chunked_gather rewrites it per chunk.
135
137
  *
136
138
  * Mask handling (m0):
137
- * Built once in acquire_chunked at full size (= n_kernel bytes).
138
- * Macros that want per-cell mask read at m0[chunk_off + k].
139
- * Full-size m0 mirrors the whole path; per-chunk mask gather is a
140
- * future optim.
139
+ * Chunk-sized (= chunk_n_max bytes), re-gathered per chunk in
140
+ * ca_sweep_next_chunk. Read it at m0[k] for k < chunk_n -- it is
141
+ * indexed within the chunk, NOT by the flat cell index. (This differs
142
+ * from the whole-buffer path, where m0 spans n_kernel.)
143
+ *
144
+ * An author may WRITE m0[k] during the chunk loop (= the INOUT_MASKED
145
+ * forms' m_out). Those writes are flushed into the OUTPUT operands'
146
+ * masks when the chunk finishes -- at the top of the following
147
+ * ca_sweep_next_chunk, and once more in ca_sweep_release_chunked for
148
+ * the last chunk -- so the OUTPUT masks are created up front in
149
+ * acquire_chunked rather than propagated in one go at release.
141
150
  */
142
151
  void ca_sweep_acquire_chunked (ca_sweep_state_t *st);
143
152
  int ca_sweep_next_chunk (ca_sweep_state_t *st); /* 1 = chunk ready, 0 = done */
@@ -112,6 +112,13 @@ ca_xfer_stride_transform_fused (CArray *view,
112
112
  composed_strides, &composed_base);
113
113
  if ( !root->ptr ) return 0;
114
114
  if ( parent->bytes != root->bytes ) return 0;
115
+ /* Per-axis composition below is only meaningful for a request that walks
116
+ our own axes; a transposed / flat request over the view's addresses
117
+ (legal -- see ca_xfer_stride_request_is_axis_box in carray.h) declines
118
+ to the caller's per-cell path. */
119
+ if ( ! ca_xfer_stride_request_is_axis_box(view, starts, counts, strides) ) {
120
+ return 0;
121
+ }
115
122
 
116
123
  for ( k = 0; k < ndim; k++ ) {
117
124
  if ( view_native[k] == 0 ) return 0;