carray 2.0.1 → 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 (386) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -25
  3. data/CHANGELOG.md +338 -0
  4. data/{NEWS.md → CHANGELOG.v1.md} +3 -0
  5. data/LICENSE +1 -1
  6. data/README.md +120 -36
  7. data/carray.gemspec +32 -30
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1244 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +124 -0
  15. data/ext/ca_binop_dispatch.h +152 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +239 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4367 -0
  26. data/ext/ca_kernel_iterator.h +2596 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +839 -0
  33. data/ext/ca_obj_binop.c +948 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +967 -0
  39. data/ext/ca_obj_face.c +750 -0
  40. data/ext/ca_obj_face.h +279 -0
  41. data/ext/ca_obj_fake.c +239 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +308 -0
  45. data/ext/ca_obj_grid.c +866 -440
  46. data/ext/ca_obj_meld.c +1039 -0
  47. data/ext/ca_obj_moncmp.c +588 -0
  48. data/ext/ca_obj_monop.c +1123 -0
  49. data/ext/ca_obj_object.c +866 -296
  50. data/ext/ca_obj_record.c +470 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +593 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +624 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1306 -0
  58. data/ext/ca_obj_shift.c +231 -793
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2584 -0
  62. data/ext/ca_obj_string.c +270 -0
  63. data/ext/ca_obj_tile.c +622 -0
  64. data/ext/ca_obj_time.c +548 -0
  65. data/ext/ca_obj_timedelta.c +437 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +785 -0
  68. data/ext/ca_obj_window.c +1202 -565
  69. data/ext/ca_op_byte_swap.c +175 -0
  70. data/ext/ca_op_cmplx64.h +123 -0
  71. data/ext/ca_op_ipower.c +316 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +473 -0
  75. data/ext/ca_sweep_engine.h +166 -0
  76. data/ext/ca_transform_common.c +235 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +810 -420
  80. data/ext/carray_access.c +873 -731
  81. data/ext/carray_attribute.c +98 -329
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +376 -0
  84. data/ext/carray_build_flags.h +3 -0
  85. data/ext/carray_call_cfunc.c +2897 -874
  86. data/ext/carray_call_cfunc.h +313 -0
  87. data/ext/carray_cast.c +1264 -315
  88. data/ext/carray_cast_func.rb +81 -40
  89. data/ext/carray_class.c +53 -63
  90. data/ext/carray_config.h +28 -0
  91. data/ext/carray_conversion.c +350 -346
  92. data/ext/carray_copy.c +168 -270
  93. data/ext/carray_core.c +1396 -206
  94. data/ext/carray_count.c +312 -0
  95. data/ext/carray_data_type.c +43 -19
  96. data/ext/carray_element.c +585 -213
  97. data/ext/carray_factorize.c +2542 -0
  98. data/ext/carray_generate.c +230 -559
  99. data/ext/carray_histogram.c +490 -0
  100. data/ext/carray_hold.c +228 -0
  101. data/ext/carray_index_classifier.c +1021 -0
  102. data/ext/carray_index_classifier.h +27 -0
  103. data/ext/carray_internal.h +136 -0
  104. data/ext/carray_kernels_bincmp.c +4446 -0
  105. data/ext/carray_kernels_binop.c +11001 -0
  106. data/ext/carray_kernels_init.c +1131 -0
  107. data/ext/carray_kernels_map.c +3467 -0
  108. data/ext/carray_kernels_moncmp.c +2097 -0
  109. data/ext/carray_kernels_monop.c +18313 -0
  110. data/ext/carray_kernels_reduce_aggregate.c +25837 -0
  111. data/ext/carray_kernels_reduce_boolean.c +330 -0
  112. data/ext/carray_kernels_reduce_cumulative.c +14593 -0
  113. data/ext/carray_kernels_reduce_extreme.c +16948 -0
  114. data/ext/carray_kernels_reduce_variance.c +3910 -0
  115. data/ext/carray_kernels_scan.c +3693 -0
  116. data/ext/carray_kernels_search.c +32138 -0
  117. data/ext/carray_kernels_sort.c +10626 -0
  118. data/ext/carray_kernels_triop.c +1392 -0
  119. data/ext/carray_lazy.c +737 -0
  120. data/ext/carray_loop.c +88 -200
  121. data/ext/carray_mask.c +853 -158
  122. data/ext/carray_math_kernel.h +120 -0
  123. data/ext/carray_mathfunc.c +10 -241
  124. data/ext/carray_median_percentile.c +1257 -0
  125. data/ext/carray_memory_view.c +1650 -0
  126. data/ext/carray_operator.c +1525 -320
  127. data/ext/carray_order.c +664 -1394
  128. data/ext/carray_partition.c +416 -0
  129. data/ext/carray_random.c +518 -0
  130. data/ext/carray_scatter.c +357 -0
  131. data/ext/carray_slab.c +1219 -0
  132. data/ext/carray_slab.h +84 -0
  133. data/ext/carray_sort.c +829 -0
  134. data/ext/carray_sort_kernel.c +620 -0
  135. data/ext/carray_struct.c +695 -0
  136. data/ext/carray_test.c +343 -229
  137. data/ext/carray_undef.c +34 -17
  138. data/ext/carray_utils.c +175 -74
  139. data/ext/extconf.rb +234 -55
  140. data/ext/mk_call_cfunc.rb +671 -0
  141. data/ext/mkkernel.rb +9096 -0
  142. data/ext/ruby_carray.c +211 -108
  143. data/ext/version.h +4 -14
  144. data/ext/version.rb +5 -13
  145. data/lib/carray/arrow_tensor.rb +401 -0
  146. data/lib/carray/attribute.rb +166 -0
  147. data/lib/carray/autoload_carray.rb +239 -0
  148. data/lib/carray/autoload_method_extension.rb +45 -0
  149. data/lib/carray/axis_group.rb +711 -0
  150. data/lib/carray/basics.rb +481 -0
  151. data/lib/carray/bincount_nd.rb +358 -0
  152. data/lib/carray/block_iterator.rb +614 -0
  153. data/lib/carray/boolean_reduce.rb +109 -0
  154. data/lib/carray/categorical.rb +561 -0
  155. data/lib/carray/categorical_iterator.rb +1084 -0
  156. data/lib/carray/complex.rb +150 -0
  157. data/lib/carray/conditional.rb +216 -0
  158. data/lib/carray/const_string.rb +228 -0
  159. data/lib/carray/construct.rb +160 -328
  160. data/lib/carray/core_extensions.rb +297 -0
  161. data/lib/carray/data_type_extension.rb +250 -0
  162. data/lib/carray/fixlen_string.rb +95 -0
  163. data/lib/carray/frame/concat.rb +132 -0
  164. data/lib/carray/frame/convert.rb +95 -0
  165. data/lib/carray/frame/csv_parser.rb +211 -0
  166. data/lib/carray/frame/frame.rb +642 -0
  167. data/lib/carray/frame/group.rb +186 -0
  168. data/lib/carray/frame/io.rb +321 -0
  169. data/lib/carray/frame/join.rb +248 -0
  170. data/lib/carray/frame/records.rb +99 -0
  171. data/lib/carray/frame/sort.rb +113 -0
  172. data/lib/carray/frame/verbs.rb +316 -0
  173. data/lib/carray/frame.rb +16 -0
  174. data/lib/carray/fuse_source.rb +123 -0
  175. data/lib/carray/fusion.rb +218 -0
  176. data/lib/carray/histogram.rb +512 -0
  177. data/lib/carray/inspect.rb +37 -26
  178. data/lib/carray/iterator.rb +58 -349
  179. data/lib/carray/lazy.rb +941 -0
  180. data/lib/carray/mask_gap_fill.rb +200 -0
  181. data/lib/carray/math.rb +78 -342
  182. data/lib/carray/meld_reduce.rb +289 -0
  183. data/lib/carray/methods/align_addr.rb +116 -0
  184. data/lib/carray/methods/bin.rb +128 -0
  185. data/lib/carray/methods/bincount.rb +87 -0
  186. data/lib/carray/methods/bit_string.rb +92 -0
  187. data/lib/carray/methods/broadcast.rb +63 -0
  188. data/lib/carray/methods/choose.rb +39 -0
  189. data/lib/carray/methods/composition.rb +280 -0
  190. data/lib/carray/methods/gather_nd.rb +206 -0
  191. data/lib/carray/methods/index.rb +39 -0
  192. data/lib/carray/methods/insert_block.rb +99 -0
  193. data/lib/carray/methods/is_in.rb +141 -0
  194. data/lib/carray/methods/join.rb +90 -0
  195. data/lib/carray/methods/locate_addr.rb +52 -0
  196. data/lib/carray/methods/mask_duplicates.rb +41 -0
  197. data/lib/carray/methods/meshgrid.rb +90 -0
  198. data/lib/carray/methods/mode.rb +126 -0
  199. data/lib/carray/methods/nunique.rb +46 -0
  200. data/lib/carray/methods/resize.rb +56 -0
  201. data/lib/carray/methods/snap.rb +161 -0
  202. data/lib/carray/methods/string_format.rb +57 -0
  203. data/lib/carray/methods/unique.rb +47 -0
  204. data/lib/carray/methods/value_counts.rb +71 -0
  205. data/lib/carray/mkmf.rb +124 -101
  206. data/lib/carray/runtime.rb +89 -0
  207. data/lib/carray/serialize.rb +478 -167
  208. data/lib/carray/slab_iterator.rb +305 -0
  209. data/lib/carray/stack.rb +291 -0
  210. data/lib/carray/string.rb +56 -180
  211. data/lib/carray/string_operation_extension.rb +289 -0
  212. data/lib/carray/struct.rb +335 -323
  213. data/lib/carray/struct_builder.rb +697 -0
  214. data/lib/carray/table.rb +41 -2
  215. data/lib/carray/time.rb +2654 -38
  216. data/lib/carray/window_iterator.rb +927 -0
  217. data/lib/carray.rb +55 -57
  218. data/yard-stubs/ca_obj_array.rb +385 -0
  219. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  220. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  221. data/yard-stubs/ca_obj_block.rb +73 -0
  222. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  223. data/yard-stubs/ca_obj_fake.rb +31 -0
  224. data/yard-stubs/ca_obj_farray.rb +32 -0
  225. data/yard-stubs/ca_obj_field.rb +45 -0
  226. data/yard-stubs/ca_obj_grid.rb +35 -0
  227. data/yard-stubs/ca_obj_refer.rb +72 -0
  228. data/yard-stubs/ca_obj_roll.rb +45 -0
  229. data/yard-stubs/ca_obj_shift.rb +43 -0
  230. data/yard-stubs/ca_obj_stride.rb +181 -0
  231. data/yard-stubs/ca_obj_tile.rb +29 -0
  232. data/yard-stubs/ca_obj_transpose.rb +40 -0
  233. data/yard-stubs/ca_obj_window.rb +49 -0
  234. data/yard-stubs/carray_access.rb +131 -0
  235. data/yard-stubs/carray_attribute.rb +246 -0
  236. data/yard-stubs/carray_broadcast.rb +37 -0
  237. data/yard-stubs/carray_cast.rb +489 -0
  238. data/yard-stubs/carray_class.rb +65 -0
  239. data/yard-stubs/carray_conversion.rb +76 -0
  240. data/yard-stubs/carray_copy.rb +79 -0
  241. data/yard-stubs/carray_core.rb +114 -0
  242. data/yard-stubs/carray_count.rb +79 -0
  243. data/yard-stubs/carray_element.rb +108 -0
  244. data/yard-stubs/carray_generate.rb +66 -0
  245. data/yard-stubs/carray_lazy.rb +23 -0
  246. data/yard-stubs/carray_loop.rb +140 -0
  247. data/yard-stubs/carray_mask.rb +259 -0
  248. data/yard-stubs/carray_math.rb +132 -0
  249. data/yard-stubs/carray_mathfunc.rb +45 -0
  250. data/yard-stubs/carray_median_percentile.rb +89 -0
  251. data/yard-stubs/carray_memory_view.rb +163 -0
  252. data/yard-stubs/carray_order.rb +312 -0
  253. data/yard-stubs/carray_random.rb +89 -0
  254. data/yard-stubs/carray_scatter.rb +106 -0
  255. data/yard-stubs/carray_slab.rb +57 -0
  256. data/yard-stubs/carray_sort.rb +163 -0
  257. data/yard-stubs/carray_test.rb +85 -0
  258. data/yard-stubs/carray_undef.rb +64 -0
  259. data/yard-stubs/carray_utils.rb +97 -0
  260. data/yard-stubs/ruby_carray.rb +193 -0
  261. metadata +220 -138
  262. data/Rakefile +0 -51
  263. data/TODO.md +0 -18
  264. data/ext/ca_iter_block.c +0 -257
  265. data/ext/ca_iter_dimension.c +0 -299
  266. data/ext/ca_iter_window.c +0 -214
  267. data/ext/ca_obj_mapping.c +0 -644
  268. data/ext/ca_obj_unbound_repeat.c +0 -529
  269. data/ext/carray_iterator.c +0 -641
  270. data/ext/carray_math.rb +0 -850
  271. data/ext/carray_numeric.c +0 -259
  272. data/ext/carray_sort_addr.c +0 -254
  273. data/ext/carray_stat.c +0 -2100
  274. data/ext/carray_stat_proc.rb +0 -1999
  275. data/ext/mkmath.rb +0 -741
  276. data/ext/ruby_ccomplex.c +0 -509
  277. data/ext/ruby_float_func.c +0 -86
  278. data/lib/carray/array.rb +0 -8
  279. data/lib/carray/autoload/autoload_base.rb +0 -19
  280. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  281. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  282. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  283. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  284. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  285. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  286. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  287. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  288. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  289. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  290. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  291. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  292. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  293. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  294. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  295. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  296. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  297. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  298. data/lib/carray/autoload.rb +0 -141
  299. data/lib/carray/basic.rb +0 -191
  300. data/lib/carray/broadcast.rb +0 -101
  301. data/lib/carray/compose.rb +0 -315
  302. data/lib/carray/convert.rb +0 -115
  303. data/lib/carray/info.rb +0 -110
  304. data/lib/carray/io/imagemagick.rb +0 -235
  305. data/lib/carray/mask.rb +0 -102
  306. data/lib/carray/math/histogram.rb +0 -177
  307. data/lib/carray/math/recurrence.rb +0 -93
  308. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  309. data/lib/carray/object/ca_obj_link.rb +0 -50
  310. data/lib/carray/object/ca_obj_pack.rb +0 -99
  311. data/lib/carray/obsolete.rb +0 -256
  312. data/lib/carray/ordering.rb +0 -181
  313. data/lib/carray/testing.rb +0 -51
  314. data/lib/carray/transform.rb +0 -109
  315. data/mailmap +0 -1
  316. data/misc/Methods.ja.md +0 -182
  317. data/misc/NOTE +0 -51
  318. data/spec/Classes/CABitfield_spec.rb +0 -58
  319. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  320. data/spec/Classes/CABlock_spec.rb +0 -205
  321. data/spec/Classes/CAField_spec.rb +0 -39
  322. data/spec/Classes/CAGrid_spec.rb +0 -75
  323. data/spec/Classes/CAMap_spec.rb +0 -0
  324. data/spec/Classes/CAMapping_spec.rb +0 -105
  325. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  326. data/spec/Classes/CAObject_spec.rb +0 -33
  327. data/spec/Classes/CARefer_spec.rb +0 -93
  328. data/spec/Classes/CARepeat_spec.rb +0 -65
  329. data/spec/Classes/CASelect_spec.rb +0 -22
  330. data/spec/Classes/CAShift_spec.rb +0 -16
  331. data/spec/Classes/CAStruct_spec.rb +0 -71
  332. data/spec/Classes/CATranspose_spec.rb +0 -60
  333. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  334. data/spec/Classes/CAWindow_spec.rb +0 -54
  335. data/spec/Classes/CAWrap_spec.rb +0 -8
  336. data/spec/Classes/CArray_spec.rb +0 -184
  337. data/spec/Classes/CScalar_spec.rb +0 -55
  338. data/spec/Features/feature_130_spec.rb +0 -19
  339. data/spec/Features/feature_attributes_spec.rb +0 -280
  340. data/spec/Features/feature_boolean_spec.rb +0 -98
  341. data/spec/Features/feature_broadcast.rb +0 -116
  342. data/spec/Features/feature_cast_function.rb +0 -19
  343. data/spec/Features/feature_cast_spec.rb +0 -33
  344. data/spec/Features/feature_class_spec.rb +0 -84
  345. data/spec/Features/feature_complex_spec.rb +0 -42
  346. data/spec/Features/feature_composite_spec.rb +0 -124
  347. data/spec/Features/feature_convert_spec.rb +0 -46
  348. data/spec/Features/feature_copy_spec.rb +0 -123
  349. data/spec/Features/feature_creation_spec.rb +0 -84
  350. data/spec/Features/feature_element_spec.rb +0 -144
  351. data/spec/Features/feature_extream_spec.rb +0 -54
  352. data/spec/Features/feature_generate_spec.rb +0 -74
  353. data/spec/Features/feature_index_spec.rb +0 -69
  354. data/spec/Features/feature_mask_spec.rb +0 -580
  355. data/spec/Features/feature_math_spec.rb +0 -97
  356. data/spec/Features/feature_order_spec.rb +0 -146
  357. data/spec/Features/feature_ref_store_spec.rb +0 -209
  358. data/spec/Features/feature_serialization_spec.rb +0 -125
  359. data/spec/Features/feature_stat_spec.rb +0 -397
  360. data/spec/Features/feature_virtual_spec.rb +0 -48
  361. data/spec/Features/method_eq_spec.rb +0 -81
  362. data/spec/Features/method_is_nan_spec.rb +0 -12
  363. data/spec/Features/method_map_spec.rb +0 -54
  364. data/spec/Features/method_max_with.rb +0 -20
  365. data/spec/Features/method_min_with.rb +0 -19
  366. data/spec/Features/method_ne_spec.rb +0 -18
  367. data/spec/Features/method_project_spec.rb +0 -188
  368. data/spec/Features/method_ref_spec.rb +0 -27
  369. data/spec/Features/method_round_spec.rb +0 -11
  370. data/spec/Features/method_s_linspace_spec.rb +0 -48
  371. data/spec/Features/method_s_span_spec.rb +0 -14
  372. data/spec/Features/method_seq_spec.rb +0 -47
  373. data/spec/Features/method_sort_with.rb +0 -43
  374. data/spec/Features/method_sorted_with.rb +0 -29
  375. data/spec/Features/method_span_spec.rb +0 -42
  376. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  377. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  378. data/spec/spec_all.rb +0 -10
  379. data/utils/ca_ase.rb +0 -21
  380. data/utils/ca_methods.rb +0 -15
  381. data/utils/cast_checker.rb +0 -30
  382. data/utils/convert_test.rb +0 -73
  383. data/utils/extract_yard.rb +0 -22
  384. data/utils/guess_shape.rb +0 -76
  385. data/utils/monkey_patch_methods.rb +0 -62
  386. data/utils/remove_resource_fork.sh +0 -5
@@ -0,0 +1,401 @@
1
+ # Apache Arrow "tensor IPC" reader / writer for CArray. [experimental]
2
+ #
3
+ # This surface is experimental: the method names, the module namespace, and
4
+ # the packaging (it may later be extracted into a standalone, array-library-
5
+ # neutral gem) are not yet frozen and may change in a future release.
6
+ #
7
+ # This is an interoperability layer, not an archival format: it exchanges
8
+ # a dense numeric array with the Arrow ecosystem (pyarrow.ipc.write_tensor /
9
+ # read_tensor and equivalents) using Arrow's encapsulated tensor message.
10
+ # For faithful, reproducible persistence of a CArray -- mask, attributes,
11
+ # data_class and all -- use CArray.save / CArray.load (the _CARRAY3 format).
12
+ #
13
+ # The on-disk shape is: a 0xFFFFFFFF continuation marker, an int32 metadata
14
+ # length, a FlatBuffers Message whose header is a Tensor (element type, shape,
15
+ # byte strides, and a Buffer descriptor), padding to an 8-byte boundary, then
16
+ # the raw contiguous element bytes. Only the numeric types Arrow tensors
17
+ # admit are supported (int8..int64, uint8..uint64, float32, float64); boolean,
18
+ # complex, fixlen and object arrays have no Arrow tensor representation and are
19
+ # rejected. A masked CArray is rejected too, mirroring the MemoryView policy:
20
+ # Arrow tensors carry no validity concept, so the caller must be explicit
21
+ # (ca.value to drop the mask, ca.unmask_copy(fill) to bake it in, or persist
22
+ # the mask separately through CArray.save).
23
+
24
+ require "stringio"
25
+
26
+ class CArray
27
+ # Reader / writer for Arrow's encapsulated tensor IPC message, for
28
+ # exchanging a dense numeric array with the Arrow ecosystem
29
+ # (`pyarrow.ipc.write_tensor` / `read_tensor` and equivalents).
30
+ #
31
+ # Experimental: the method names, this namespace and the packaging are not
32
+ # frozen, and the layer may later move to a standalone, array-library-neutral
33
+ # gem. It is an interoperability format, not an archival one — an Arrow
34
+ # tensor carries no mask, attributes or data_class, so `CArray.save` /
35
+ # `CArray.load` remain the way to persist a CArray faithfully.
36
+ #
37
+ # Only the numeric types an Arrow tensor admits are supported (int8..int64,
38
+ # uint8..uint64, float32, float64). Boolean, complex, fixlen and object
39
+ # arrays are rejected, as is a masked array — Arrow tensors have no validity
40
+ # concept, so the caller must be explicit (`ca.value` to drop the mask,
41
+ # `ca.unmask_copy(fill)` to bake it in, or save the mask separately).
42
+ module ArrowTensor
43
+
44
+ # CArray data_type symbol -> Arrow element-type descriptor.
45
+ # [:float, precision] precision: 1 = single, 2 = double
46
+ # [:int, bit_width, signed?]
47
+ DTYPE_TO_ARROW = {
48
+ float32: [:float, 1], float64: [:float, 2],
49
+ int8: [:int, 8, true], int16: [:int, 16, true],
50
+ int32: [:int, 32, true], int64: [:int, 64, true],
51
+ uint8: [:int, 8, false], uint16:[:int, 16, false],
52
+ uint32:[:int, 32, false], uint64:[:int, 64, false],
53
+ }.freeze
54
+
55
+ module_function
56
+
57
+ # Write +ca+ to +io+ as an Arrow tensor IPC message.
58
+ def write (ca, io)
59
+ if ca.has_mask?
60
+ raise ArgumentError,
61
+ "cannot write a masked CArray as an Arrow tensor " \
62
+ "(Arrow tensors have no validity concept); pass ca.value to " \
63
+ "drop the mask, ca.unmask_copy(fill) to fill it, or use " \
64
+ "CArray.save for a mask-preserving round-trip"
65
+ end
66
+ arrow = DTYPE_TO_ARROW[ca.data_type]
67
+ unless arrow
68
+ raise ArgumentError,
69
+ "cannot write a #{ca.data_type} CArray as an Arrow tensor " \
70
+ "(Arrow tensors are numeric only: int8..int64, uint8..uint64, " \
71
+ "float32, float64)"
72
+ end
73
+ raise ArgumentError, "cannot write a 0-dimensional array" if ca.ndim < 1
74
+
75
+ shape = ca.shape
76
+ elt = ca.bytes
77
+ strides = row_major_strides(shape, elt)
78
+
79
+ body = StringIO.new("".b)
80
+ ca.dump_binary(body) # raw contiguous element bytes, row-major
81
+ body = body.string
82
+
83
+ meta = build_metadata(arrow, shape, strides, body.bytesize)
84
+ pad = (8 - ((8 + meta.bytesize) % 8)) % 8
85
+ meta += "\0".b * pad
86
+
87
+ io.write([0xFFFFFFFF].pack("V"))
88
+ io.write([meta.bytesize].pack("l<"))
89
+ io.write(meta)
90
+ io.write(body)
91
+ ca
92
+ end
93
+
94
+ # Read an Arrow tensor IPC message from +io+ and return a new CArray.
95
+ def read (io)
96
+ b = io.respond_to?(:read) ? io.read : io.to_s
97
+ b = b.b
98
+ meta = parse_metadata(b)
99
+ type = meta[:data_type]
100
+ unless CArray.respond_to?(:data_type_code) && DTYPE_TO_ARROW.key?(type)
101
+ raise ArgumentError,
102
+ "Arrow tensor element type #{type.inspect} has no CArray " \
103
+ "counterpart"
104
+ end
105
+ build_carray(type, meta[:shape], meta[:strides], meta[:body])
106
+ end
107
+
108
+ # ---- helpers ----------------------------------------------------------
109
+
110
+ def row_major_strides (shape, elt)
111
+ s = Array.new(shape.length)
112
+ acc = elt
113
+ (shape.length - 1).downto(0) do |i|
114
+ s[i] = acc
115
+ acc *= shape[i]
116
+ end
117
+ s
118
+ end
119
+ private_class_method :row_major_strides
120
+
121
+ def build_carray (type, shape, strides, body)
122
+ elt = CArray.new(type, [1]).bytes
123
+ rmaj = row_major_strides(shape, elt)
124
+ if strides == rmaj
125
+ ca = CArray.new(type, shape)
126
+ ca.load_binary(StringIO.new(body))
127
+ return ca
128
+ end
129
+ cmaj = row_major_strides(shape.reverse, elt).reverse
130
+ if strides == cmaj
131
+ t = CArray.new(type, shape.reverse)
132
+ t.load_binary(StringIO.new(body))
133
+ return t.transpose(*(0...shape.length).to_a.reverse).to_ca
134
+ end
135
+ # General strided layout (rare): gather element by element.
136
+ gather_strided(type, shape, strides, body)
137
+ end
138
+ private_class_method :build_carray
139
+
140
+ def gather_strided (type, shape, strides, body)
141
+ elt = CArray.new(type, [1]).bytes
142
+ code = UNPACK[type]
143
+ ca = CArray.new(type, shape)
144
+ elements = shape.inject(1, :*)
145
+ idx = Array.new(shape.length, 0)
146
+ elements.times do
147
+ off = 0
148
+ idx.each_with_index { |v, k| off += v * strides[k] }
149
+ ca[*idx] = body[off, elt].unpack1(code)
150
+ k = shape.length - 1
151
+ while k >= 0
152
+ idx[k] += 1
153
+ break if idx[k] < shape[k]
154
+ idx[k] = 0
155
+ k -= 1
156
+ end
157
+ end
158
+ ca
159
+ end
160
+ private_class_method :gather_strided
161
+
162
+ # @!visibility private
163
+ UNPACK = {
164
+ float32:"e", float64:"E",
165
+ int8:"c", int16:"s<", int32:"l<", int64:"q<",
166
+ uint8:"C", uint16:"v", uint32:"V", uint64:"Q<",
167
+ }.freeze
168
+
169
+ # ---- FlatBuffers Message encoder (header = Tensor) --------------------
170
+
171
+ # Minimal back-to-front FlatBuffers builder, specialised to the fields the
172
+ # Tensor message uses (scalars, tables, vectors, one inline struct).
173
+ #
174
+ # @!visibility private
175
+ class Builder
176
+ def initialize
177
+ @buf = "".b
178
+ @minalign = 1
179
+ @vt = nil
180
+ @obj_start = nil
181
+ end
182
+
183
+ # @!visibility private
184
+ def offset; @buf.bytesize; end
185
+
186
+ # @!visibility private
187
+ def prep (size, additional)
188
+ @minalign = size if size > @minalign
189
+ align = ((~(@buf.bytesize + additional)) + 1) & (size - 1)
190
+ @buf.prepend("\0".b * align) if align > 0
191
+ end
192
+
193
+ # @!visibility private
194
+ def u8 (x); @buf.prepend([x].pack("C")); end
195
+ # @!visibility private
196
+ def i16 (x); @buf.prepend([x].pack("s<")); end
197
+ # @!visibility private
198
+ def u16 (x); @buf.prepend([x].pack("v")); end
199
+ # @!visibility private
200
+ def u32 (x); @buf.prepend([x].pack("V")); end
201
+ # @!visibility private
202
+ def i32 (x); @buf.prepend([x].pack("l<")); end
203
+ # @!visibility private
204
+ def i64 (x); @buf.prepend([x].pack("q<")); end
205
+
206
+ # @!visibility private
207
+ def uoffset (off)
208
+ prep(4, 0)
209
+ u32(offset() - off + 4)
210
+ end
211
+
212
+ # @!visibility private
213
+ def slot (v); @vt[v] = offset(); end
214
+
215
+ # @!visibility private
216
+ def slot_i16 (v, x); prep(2,0); i16(x); slot(v); end
217
+ # @!visibility private
218
+ def slot_u8 (v, x); prep(1,0); u8(x); slot(v); end
219
+ # @!visibility private
220
+ def slot_i32 (v, x); prep(4,0); i32(x); slot(v); end
221
+ # @!visibility private
222
+ def slot_i64 (v, x); prep(8,0); i64(x); slot(v); end
223
+ # @!visibility private
224
+ def slot_bool(v, x); prep(1,0); u8(x ? 1 : 0); slot(v); end
225
+ # @!visibility private
226
+ def slot_uoffset (v, off); uoffset(off); slot(v); end
227
+
228
+ # @!visibility private
229
+ def start_object (n); @vt = Array.new(n, 0); @obj_start = offset(); end
230
+
231
+ # @!visibility private
232
+ def end_object
233
+ prep(4, 0)
234
+ i32(0) # soffset placeholder
235
+ obj = offset()
236
+ n = @vt.length
237
+ n -= 1 while n > 0 && @vt[n-1] == 0
238
+ (n-1).downto(0) do |i|
239
+ u16(@vt[i] == 0 ? 0 : (obj - @vt[i]))
240
+ end
241
+ u16(obj - @obj_start) # referenced table size
242
+ u16((n + 2) * 2) # vtable size
243
+ soff = offset() - obj
244
+ @buf[@buf.bytesize - obj, 4] = [soff].pack("l<")
245
+ obj
246
+ end
247
+
248
+ # @!visibility private
249
+ def start_vector (elem, count, align); prep(4, elem*count); prep(align, elem*count); end
250
+ # @!visibility private
251
+ def end_vector (count); u32(count); offset(); end
252
+
253
+ # @!visibility private
254
+ def finish (root)
255
+ prep(@minalign, 4)
256
+ uoffset(root)
257
+ @buf
258
+ end
259
+ end
260
+
261
+ def build_metadata (arrow, shape, strides, body_len)
262
+ fb = Builder.new
263
+ tag = arrow[0] == :float ? 3 : 2
264
+
265
+ type_off =
266
+ if arrow[0] == :float
267
+ fb.start_object(1)
268
+ fb.slot_i16(0, arrow[1]) # precision
269
+ fb.end_object
270
+ else
271
+ fb.start_object(2)
272
+ fb.slot_i32(0, arrow[1]) # bitWidth
273
+ fb.slot_bool(1, arrow[2]) # is_signed
274
+ fb.end_object
275
+ end
276
+
277
+ dim_offs = shape.map do |sz|
278
+ fb.start_object(2)
279
+ fb.slot_i64(0, sz) # TensorDim.size (name omitted)
280
+ fb.end_object
281
+ end
282
+ fb.start_vector(4, dim_offs.length, 4)
283
+ dim_offs.reverse_each { |o| fb.uoffset(o) }
284
+ shape_vec = fb.end_vector(dim_offs.length)
285
+
286
+ fb.start_vector(8, strides.length, 8)
287
+ strides.reverse_each { |s| fb.i64(s) }
288
+ strides_vec = fb.end_vector(strides.length)
289
+
290
+ fb.start_object(5) # Tensor: type_type,type,shape,strides,data
291
+ fb.prep(8, 16) # data: inline Buffer struct
292
+ fb.i64(body_len) # length
293
+ fb.i64(0) # offset
294
+ fb.slot(4)
295
+ fb.slot_uoffset(3, strides_vec)
296
+ fb.slot_uoffset(2, shape_vec)
297
+ fb.slot_uoffset(1, type_off)
298
+ fb.slot_u8(0, tag) # type_type
299
+ tensor_off = fb.end_object
300
+
301
+ fb.start_object(5) # Message: version,header_type,header,bodyLength,custom
302
+ fb.slot_i64(3, body_len)
303
+ fb.slot_uoffset(2, tensor_off)
304
+ fb.slot_u8(1, 4) # header_type = Tensor
305
+ fb.slot_i16(0, 4) # metadata version V5
306
+ msg_off = fb.end_object
307
+
308
+ fb.finish(msg_off)
309
+ end
310
+ private_class_method :build_metadata
311
+
312
+ # ---- FlatBuffers Message decoder --------------------------------------
313
+
314
+ def parse_metadata (b)
315
+ u16 = ->(o){ b[o,2].unpack1("v") }
316
+ i32 = ->(o){ b[o,4].unpack1("l<") }
317
+ u32 = ->(o){ b[o,4].unpack1("V") }
318
+ i64 = ->(o){ b[o,8].unpack1("q<") }
319
+ field = ->(t,f){ vt = t - i32.(t); vts = u16.(vt); slot = 4 + f*2
320
+ next 0 if slot >= vts; vo = u16.(vt+slot); vo==0 ? 0 : t+vo }
321
+ deref = ->(pos){ pos + u32.(pos) }
322
+
323
+ raise ArgumentError, "not an Arrow IPC message" unless u32.(0) == 0xffffffff
324
+ meta_len = i32.(4)
325
+ fb = 8
326
+ body_off = 8 + meta_len
327
+
328
+ msg = fb + i32.(fb)
329
+ htag = (p = field.(msg,1); p == 0 ? 0 : b.getbyte(p))
330
+ raise ArgumentError, "not a tensor message" unless htag == 4
331
+ hpos = deref.(field.(msg,2))
332
+
333
+ type_tag = b.getbyte(field.(hpos,0))
334
+ type_tbl = deref.(field.(hpos,1))
335
+ shp_vec = deref.(field.(hpos,2)); n = u32.(shp_vec)
336
+ shape = (0...n).map { |i| dp = deref.(shp_vec + 4 + i*4); i64.(field.(dp,0)) }
337
+ sv_slot = field.(hpos,3)
338
+ strides =
339
+ if sv_slot == 0
340
+ row_major_strides(shape, CArray.new(arrow_symbol(type_tag, type_tbl, u16, i32, b), [1]).bytes)
341
+ else
342
+ sv = deref.(sv_slot); m = u32.(sv); (0...m).map { |i| i64.(sv + 4 + i*8) }
343
+ end
344
+ dpos = field.(hpos,4)
345
+ data_off = i64.(dpos); data_len = i64.(dpos+8)
346
+
347
+ { data_type: arrow_symbol(type_tag, type_tbl, u16, i32, b),
348
+ shape: shape, strides: strides,
349
+ body: b[body_off + data_off, data_len] }
350
+ end
351
+ private_class_method :parse_metadata
352
+
353
+ def arrow_symbol (tag, tbl, u16, i32, b)
354
+ case tag
355
+ when 3
356
+ { 0=>:float16, 1=>:float32, 2=>:float64 }[u16.(tbl_field(tbl, 0, i32, u16))] ||
357
+ (raise ArgumentError, "unsupported Arrow float precision")
358
+ when 2
359
+ bw = i32.(tbl_field(tbl, 0, i32, u16))
360
+ sf = tbl_field(tbl, 1, i32, u16)
361
+ signed = sf == 0 ? false : (b.getbyte(sf) != 0) # is_signed FlatBuffers default = false
362
+ (signed ? "int#{bw}" : "uint#{bw}").to_sym
363
+ else
364
+ raise ArgumentError, "unsupported Arrow tensor element type (tag=#{tag})"
365
+ end
366
+ end
367
+ private_class_method :arrow_symbol
368
+
369
+ def tbl_field (t, f, i32, u16)
370
+ vt = t - i32.(t); vts = u16.(vt); slot = 4 + f*2
371
+ return 0 if slot >= vts
372
+ vo = u16.(vt + slot); vo == 0 ? 0 : t + vo
373
+ end
374
+ private_class_method :tbl_field
375
+
376
+ end
377
+
378
+ # Reads an Arrow tensor IPC file and returns it as a new CArray.
379
+ #
380
+ # Experimental, and the name is provisional.
381
+ #
382
+ # @param filename [String] path to the message.
383
+ # @return [CArray]
384
+ # @see ArrowTensor
385
+ def self.load_arrow_tensor (filename)
386
+ File.open(filename, "rb") { |io| ArrowTensor.read(io) }
387
+ end
388
+
389
+ # Writes `self` to `filename` as an Arrow tensor IPC message.
390
+ #
391
+ # Experimental, and the name is provisional. Rejects a masked or
392
+ # non-numeric array; see {ArrowTensor} for the type policy.
393
+ #
394
+ # @param filename [String] path to write.
395
+ # @return [self]
396
+ # @see ArrowTensor
397
+ def save_arrow_tensor (filename)
398
+ File.open(filename, "wb") { |io| ArrowTensor.write(self, io) }
399
+ self
400
+ end
401
+ end
@@ -0,0 +1,166 @@
1
+ # User-defined arbitrary key/value metadata attached to a CArray
2
+ # instance.
3
+ #
4
+ # Surface:
5
+ # ca.attr(:unit) # => getter; nil if absent
6
+ # ca.set_attr(:unit, "m/s") # => setter; writes to self's @attr
7
+ # ca.attrs # => frozen shallow clone of the full Hash
8
+ # ca.has_attr? # => any attribute present?
9
+ # ca.has_attr?(:unit) # => specific key present?
10
+ #
11
+ # Key: accepts Symbol or String on input; stored as String.
12
+ # Value: JSON-compatible plus non-finite Floats (String / Numeric,
13
+ # including Infinity / NaN / -Infinity / true / false / nil /
14
+ # Array / Hash<String|Symbol, ...>). Symbol values are
15
+ # coerced to String on store. Disallowed types raise
16
+ # TypeError on set. Non-finite Floats are accepted so
17
+ # imported metadata (e.g. a NetCDF _FillValue of Infinity)
18
+ # is not rejected; the serialize trailer is YAML, whose
19
+ # .inf / .nan literals round-trip them (serialize.rb).
20
+ #
21
+ # View semantics (per-key fallback):
22
+ # Getter side (attr / has_attr?) walks the parent chain
23
+ # *per key*: if the receiver's @attr has the key, return it;
24
+ # otherwise walk up. Each view shadows its parents one key at
25
+ # a time, so writing :slice on a view does not hide :unit
26
+ # inherited from the parent entity.
27
+ # `attrs` returns the effective merged Hash (deeper writes
28
+ # override shallower ones), frozen.
29
+ # Setter side always writes to self's @attr.
30
+
31
+ class CArray
32
+
33
+ # @!group Attributes
34
+
35
+ # @overload attr(key)
36
+ # Returns the value of the attribute `key`, or `nil` when the key
37
+ # is absent. Walks the parent chain per key: the deepest view
38
+ # that has an entry for `key` wins.
39
+ # @param key [Symbol, String] attribute key.
40
+ # @return [Object, nil]
41
+ def attr (key)
42
+ k = attr_normalize_key(key)
43
+ attr_each_chain do |h|
44
+ return h[k] if h.key?(k)
45
+ end
46
+ nil
47
+ end
48
+
49
+ # @overload set_attr(key, value)
50
+ # Sets attribute `key` to `value` on `self` (not on any parent).
51
+ # Values are validated as JSON-compatible plus non-finite Floats
52
+ # (String / Numeric incl. Infinity / NaN / true / false / nil /
53
+ # Symbol / Array / Hash); Symbol values are coerced to String on
54
+ # store.
55
+ # @param key [Symbol, String] attribute key.
56
+ # @param value [Object] JSON-compatible value.
57
+ # @return [Object] the coerced stored value.
58
+ # @raise [TypeError] when `key` or `value` is not accepted.
59
+ def set_attr (key, value)
60
+ attr_validate_value(value)
61
+ (@attr ||= {})[attr_normalize_key(key)] = attr_coerce_value(value)
62
+ end
63
+
64
+ # @overload attrs
65
+ # Returns a frozen shallow Hash of all attributes visible on
66
+ # `self`, merged along the parent chain (deeper writes shadow
67
+ # shallower ones on a per-key basis).
68
+ # @return [Hash{String => Object}]
69
+ def attrs
70
+ merged = nil
71
+ attr_each_chain do |h|
72
+ merged ||= {}
73
+ h.each { |k, v| merged[k] = v unless merged.key?(k) }
74
+ end
75
+ (merged || {}).freeze
76
+ end
77
+
78
+ # @overload has_attr?
79
+ # Returns whether `self` (or any parent view) has any attribute
80
+ # set.
81
+ # @return [Boolean]
82
+ # @overload has_attr?(key)
83
+ # Returns whether `self` (or any parent view) has attribute `key`
84
+ # set.
85
+ # @param key [Symbol, String] attribute key.
86
+ # @return [Boolean]
87
+ def has_attr? (key = nil)
88
+ if key.nil?
89
+ attr_each_chain do |h|
90
+ return true unless h.empty?
91
+ end
92
+ false
93
+ else
94
+ k = attr_normalize_key(key)
95
+ attr_each_chain do |h|
96
+ return true if h.key?(k)
97
+ end
98
+ false
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ # Yield each non-nil @attr Hash along the parent chain (self first).
105
+ # Bounded to MAX_CHAIN_DEPTH for paranoia against unexpected cycles;
106
+ # real CArray view chains rarely exceed a handful of levels.
107
+ MAX_CHAIN_DEPTH = 64
108
+ private_constant :MAX_CHAIN_DEPTH
109
+
110
+ def attr_each_chain
111
+ ca = self
112
+ MAX_CHAIN_DEPTH.times do
113
+ if ca.instance_variable_defined?(:@attr)
114
+ h = ca.instance_variable_get(:@attr)
115
+ yield(h) if h
116
+ end
117
+ parent = ca.respond_to?(:parent) ? ca.parent : nil
118
+ return nil if parent.nil? || parent.equal?(ca)
119
+ ca = parent
120
+ end
121
+ nil
122
+ end
123
+
124
+ def attr_normalize_key (key)
125
+ case key
126
+ when String then key
127
+ when Symbol then key.to_s
128
+ else
129
+ raise TypeError, "attribute key must be String or Symbol (got #{key.class})"
130
+ end
131
+ end
132
+
133
+ def attr_validate_value (v, depth = 0)
134
+ raise ArgumentError, "attribute value nests too deep" if depth > 32
135
+ case v
136
+ when String, Integer, Float, true, false, nil, Symbol
137
+ # ok (Symbol will be coerced to String on store)
138
+ when Array
139
+ v.each { |e| attr_validate_value(e, depth + 1) }
140
+ when Hash
141
+ v.each do |k, vv|
142
+ unless k.is_a?(String) || k.is_a?(Symbol)
143
+ raise TypeError, "nested Hash keys must be String or Symbol (got #{k.class})"
144
+ end
145
+ attr_validate_value(vv, depth + 1)
146
+ end
147
+ else
148
+ raise TypeError, "attribute value must be JSON-compatible " \
149
+ "(String/Numeric/true/false/nil/Symbol/Array/Hash); got #{v.class}"
150
+ end
151
+ end
152
+
153
+ def attr_coerce_value (v)
154
+ case v
155
+ when Symbol then v.to_s
156
+ when Array then v.map { |e| attr_coerce_value(e) }
157
+ when Hash
158
+ v.each_with_object({}) do |(k, vv), h|
159
+ h[k.is_a?(Symbol) ? k.to_s : k] = attr_coerce_value(vv)
160
+ end
161
+ else
162
+ v
163
+ end
164
+ end
165
+
166
+ end