carray 3.0.3 → 3.0.5

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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +469 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_descriptor.h +27 -24
  7. data/ext/ca_axis_dispatch.c +14 -26
  8. data/ext/ca_axis_group.c +27 -27
  9. data/ext/ca_binop_dispatch.c +1 -29
  10. data/ext/ca_binop_dispatch.h +0 -1
  11. data/ext/ca_categorical_iterator.c +231 -153
  12. data/ext/ca_compare.c +9 -20
  13. data/ext/ca_composite_dispatch.c +9 -9
  14. data/ext/ca_group_iter.c +16 -17
  15. data/ext/ca_kernel_iterator.c +1240 -2000
  16. data/ext/ca_kernel_iterator.h +502 -519
  17. data/ext/ca_monop_dispatch.c +82 -59
  18. data/ext/ca_monop_dispatch.h +1 -0
  19. data/ext/ca_obj_array.c +152 -45
  20. data/ext/ca_obj_bincmp.c +25 -21
  21. data/ext/ca_obj_binop.c +16 -26
  22. data/ext/ca_obj_bitarray.c +8 -3
  23. data/ext/ca_obj_bitfield.c +63 -49
  24. data/ext/ca_obj_block.c +18 -9
  25. data/ext/ca_obj_byte_swap.c +7 -7
  26. data/ext/ca_obj_const_string.c +188 -22
  27. data/ext/ca_obj_face.c +280 -58
  28. data/ext/ca_obj_face.h +19 -0
  29. data/ext/ca_obj_fake.c +15 -29
  30. data/ext/ca_obj_farray.c +6 -3
  31. data/ext/ca_obj_field.c +30 -8
  32. data/ext/ca_obj_fixlen_string.c +2 -3
  33. data/ext/ca_obj_grid.c +455 -134
  34. data/ext/ca_obj_meld.c +172 -34
  35. data/ext/ca_obj_moncmp.c +23 -8
  36. data/ext/ca_obj_monop.c +113 -67
  37. data/ext/ca_obj_object.c +210 -52
  38. data/ext/ca_obj_record.c +15 -7
  39. data/ext/ca_obj_reduce.c +17 -8
  40. data/ext/ca_obj_refer.c +42 -35
  41. data/ext/ca_obj_remap.c +30 -9
  42. data/ext/ca_obj_repeat.c +9 -10
  43. data/ext/ca_obj_roll.c +31 -10
  44. data/ext/ca_obj_select.c +48 -14
  45. data/ext/ca_obj_select_axis.c +15 -47
  46. data/ext/ca_obj_shift.c +41 -7
  47. data/ext/ca_obj_stack.c +180 -34
  48. data/ext/ca_obj_stride.c +190 -134
  49. data/ext/ca_obj_string.c +80 -4
  50. data/ext/ca_obj_tile.c +32 -11
  51. data/ext/ca_obj_time.c +22 -6
  52. data/ext/ca_obj_timedelta.c +22 -6
  53. data/ext/ca_obj_transpose.c +13 -7
  54. data/ext/ca_obj_triop.c +26 -32
  55. data/ext/ca_obj_window.c +267 -74
  56. data/ext/ca_op_ipower.c +4 -4
  57. data/ext/ca_sort_kernels.h +6 -3
  58. data/ext/ca_sweep_engine.c +2 -2
  59. data/ext/caframe_csv_reader.c +80 -1
  60. data/ext/carray.h +214 -75
  61. data/ext/carray_access.c +200 -99
  62. data/ext/carray_address_basis.c +343 -87
  63. data/ext/carray_attribute.c +113 -6
  64. data/ext/carray_bincount.c +10 -8
  65. data/ext/carray_broadcast.c +13 -8
  66. data/ext/carray_build_flags.h +3 -0
  67. data/ext/carray_call_cfunc.c +108 -0
  68. data/ext/carray_cast.c +375 -96
  69. data/ext/carray_cast_func.rb +90 -20
  70. data/ext/carray_class.c +4 -3
  71. data/ext/carray_config.h +28 -0
  72. data/ext/carray_conversion.c +18 -9
  73. data/ext/carray_copy.c +51 -14
  74. data/ext/carray_core.c +150 -55
  75. data/ext/carray_count.c +56 -20
  76. data/ext/carray_element.c +81 -33
  77. data/ext/carray_factorize.c +138 -84
  78. data/ext/carray_generate.c +199 -52
  79. data/ext/carray_histogram.c +63 -18
  80. data/ext/carray_hold.c +12 -15
  81. data/ext/carray_index_classifier.c +388 -176
  82. data/ext/carray_internal.h +348 -29
  83. data/ext/carray_kernels_bincmp.c +4453 -0
  84. data/ext/carray_kernels_binop.c +11368 -0
  85. data/ext/carray_kernels_init.c +1734 -0
  86. data/ext/carray_kernels_moncmp.c +2104 -0
  87. data/ext/carray_kernels_monop.c +18369 -0
  88. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  89. data/ext/carray_kernels_reduce_boolean.c +406 -0
  90. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  91. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  92. data/ext/carray_kernels_reduce_variance.c +3849 -0
  93. data/ext/carray_kernels_scan.c +4302 -0
  94. data/ext/carray_kernels_search.c +33829 -0
  95. data/ext/carray_kernels_sort.c +10897 -0
  96. data/ext/carray_kernels_triop.c +1864 -0
  97. data/ext/carray_key_windows.c +220 -0
  98. data/ext/carray_lazy.c +170 -111
  99. data/ext/carray_mask.c +337 -94
  100. data/ext/carray_median_percentile.c +82 -28
  101. data/ext/carray_memory_view.c +669 -561
  102. data/ext/carray_operator.c +129 -52
  103. data/ext/carray_order.c +604 -77
  104. data/ext/carray_partition.c +28 -27
  105. data/ext/carray_random.c +85 -31
  106. data/ext/carray_round.c +273 -0
  107. data/ext/carray_scatter.c +87 -45
  108. data/ext/carray_segment.c +3 -3
  109. data/ext/carray_slab.c +123 -37
  110. data/ext/carray_sort.c +259 -51
  111. data/ext/carray_sort_kernel.c +5 -7
  112. data/ext/carray_struct.c +54 -61
  113. data/ext/carray_test.c +26 -5
  114. data/ext/carray_time_text.c +4 -2
  115. data/ext/carray_undef.c +4 -0
  116. data/ext/carray_utils.c +309 -66
  117. data/ext/mk_call_cfunc.rb +19 -2
  118. data/ext/mkkernel.rb +403 -350
  119. data/ext/ruby_carray.c +10 -4
  120. data/ext/version.h +4 -4
  121. data/lib/carray/attribute.rb +139 -22
  122. data/lib/carray/autoload_carray.rb +8 -31
  123. data/lib/carray/autoload_method_extension.rb +14 -5
  124. data/lib/carray/axis_group.rb +255 -106
  125. data/lib/carray/basics.rb +105 -17
  126. data/lib/carray/bincount_nd.rb +12 -2
  127. data/lib/carray/block_iterator.rb +126 -23
  128. data/lib/carray/boolean_reduce.rb +17 -17
  129. data/lib/carray/categorical.rb +195 -59
  130. data/lib/carray/categorical_iterator.rb +132 -29
  131. data/lib/carray/complex.rb +5 -5
  132. data/lib/carray/conditional.rb +67 -18
  133. data/lib/carray/const_string.rb +24 -5
  134. data/lib/carray/construct.rb +15 -11
  135. data/lib/carray/data_type_extension.rb +9 -1
  136. data/lib/carray/distance_limit.rb +77 -0
  137. data/lib/carray/fixlen_string.rb +48 -3
  138. data/lib/carray/frame/column_verbs.rb +71 -0
  139. data/lib/carray/frame/concat.rb +17 -3
  140. data/lib/carray/frame/convert.rb +69 -0
  141. data/lib/carray/frame/csv_parser.rb +111 -17
  142. data/lib/carray/frame/describe.rb +3 -2
  143. data/lib/carray/frame/frame.rb +252 -28
  144. data/lib/carray/frame/group.rb +603 -101
  145. data/lib/carray/frame/io.rb +117 -28
  146. data/lib/carray/frame/join.rb +312 -51
  147. data/lib/carray/frame/records.rb +83 -7
  148. data/lib/carray/frame/reshape.rb +42 -35
  149. data/lib/carray/frame/sort.rb +7 -19
  150. data/lib/carray/frame/verbs.rb +188 -48
  151. data/lib/carray/frame/windows.rb +117 -0
  152. data/lib/carray/frame.rb +2 -0
  153. data/lib/carray/fuse_source.rb +42 -6
  154. data/lib/carray/fusion.rb +40 -16
  155. data/lib/carray/histogram.rb +6 -1
  156. data/lib/carray/inspect.rb +31 -8
  157. data/lib/carray/iterator.rb +85 -7
  158. data/lib/carray/key_windows.rb +620 -0
  159. data/lib/carray/lazy.rb +217 -183
  160. data/lib/carray/mask_gap_fill.rb +63 -57
  161. data/lib/carray/meld_reduce.rb +12 -14
  162. data/lib/carray/methods/bin.rb +26 -18
  163. data/lib/carray/methods/bincount.rb +16 -0
  164. data/lib/carray/methods/bit_string.rb +1 -1
  165. data/lib/carray/methods/choose.rb +16 -3
  166. data/lib/carray/methods/composition.rb +3 -1
  167. data/lib/carray/methods/gather_nd.rb +14 -5
  168. data/lib/carray/methods/insert_block.rb +2 -2
  169. data/lib/carray/methods/is_between.rb +59 -0
  170. data/lib/carray/methods/is_in.rb +61 -37
  171. data/lib/carray/methods/join.rb +1 -6
  172. data/lib/carray/methods/lag.rb +49 -0
  173. data/lib/carray/methods/locate_addr.rb +47 -51
  174. data/lib/carray/methods/mode.rb +1 -1
  175. data/lib/carray/methods/nunique.rb +11 -6
  176. data/lib/carray/methods/resize.rb +3 -3
  177. data/lib/carray/methods/value_counts.rb +4 -11
  178. data/lib/carray/record_order.rb +3 -3
  179. data/lib/carray/rng.rb +3 -0
  180. data/lib/carray/runtime.rb +53 -8
  181. data/lib/carray/segment_iterator.rb +323 -88
  182. data/lib/carray/serialize.rb +45 -14
  183. data/lib/carray/slab_iterator.rb +50 -0
  184. data/lib/carray/stack.rb +15 -12
  185. data/lib/carray/string.rb +113 -5
  186. data/lib/carray/string_operation_extension.rb +21 -6
  187. data/lib/carray/struct.rb +9 -21
  188. data/lib/carray/struct_builder.rb +24 -11
  189. data/lib/carray/time.rb +819 -158
  190. data/lib/carray/window_iterator.rb +122 -16
  191. data/lib/carray.rb +7 -0
  192. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  193. data/yard-stubs/ca_obj_field.rb +4 -0
  194. data/yard-stubs/ca_obj_grid.rb +21 -10
  195. data/yard-stubs/ca_obj_refer.rb +2 -1
  196. data/yard-stubs/ca_obj_roll.rb +2 -2
  197. data/yard-stubs/ca_obj_shift.rb +5 -1
  198. data/yard-stubs/ca_obj_tile.rb +1 -1
  199. data/yard-stubs/ca_obj_transpose.rb +5 -4
  200. data/yard-stubs/ca_obj_window.rb +8 -3
  201. data/yard-stubs/carray_access.rb +6 -1
  202. data/yard-stubs/carray_attribute.rb +3 -6
  203. data/yard-stubs/carray_cast.rb +12 -0
  204. data/yard-stubs/carray_conversion.rb +7 -2
  205. data/yard-stubs/carray_copy.rb +4 -2
  206. data/yard-stubs/carray_count.rb +4 -4
  207. data/yard-stubs/carray_element.rb +17 -10
  208. data/yard-stubs/carray_generate.rb +21 -1
  209. data/yard-stubs/carray_mask.rb +32 -3
  210. data/yard-stubs/carray_math.rb +41 -5
  211. data/yard-stubs/carray_median_percentile.rb +5 -2
  212. data/yard-stubs/carray_memory_view.rb +40 -23
  213. data/yard-stubs/carray_order.rb +129 -22
  214. data/yard-stubs/carray_random.rb +13 -4
  215. data/yard-stubs/carray_scatter.rb +17 -10
  216. data/yard-stubs/carray_slab.rb +9 -2
  217. data/yard-stubs/carray_sort.rb +6 -5
  218. data/yard-stubs/carray_test.rb +5 -3
  219. metadata +25 -3
  220. data/lib/carray/arrow_tensor.rb +0 -401
  221. data/lib/carray/table.rb +0 -108
@@ -16,13 +16,17 @@ class CArray
16
16
  # Shared contract:
17
17
  #
18
18
  # - `addrs` is a CArray of any integer type (coerced to
19
- # `CA_SIZE`) or a Ruby Array.
19
+ # `CA_SIZE`) or a Ruby Array. A float or complex CArray is
20
+ # refused with `IndexError`, as `self[addrs]` refuses it.
20
21
  # - `vals` is a CArray of length matching `addrs` (coerced to
21
- # `self.data_type`), or a Numeric scalar broadcast to all
22
- # addresses. A complex `self` also takes a Complex scalar.
23
- # - Out-of-range `addrs[i]` (`< 0` or `>= self.elements`) raises
24
- # `IndexError`. The pairs before it stay applied, through a view
25
- # as well.
22
+ # `self.data_type`), or a scalar broadcast to all addresses: any
23
+ # Numeric, `true` / `false`, or `UNDEF`. The scalar is converted as
24
+ # `self[i] = v` converts it, so a value that does not fit the type
25
+ # raises (`RangeError`, `FloatDomainError` for NaN into an integer
26
+ # type). A scalar `UNDEF` is a masked value.
27
+ # - A negative `addrs[i]` counts from the end. One out of range
28
+ # raises `IndexError`; the pairs before it stay applied, through a
29
+ # view as well.
26
30
  # - `self.data_type` must be numeric. Complex is accepted by
27
31
  # every variant except {#scatter_min!} and {#scatter_max!},
28
32
  # since complex values have no order.
@@ -100,12 +104,15 @@ class CArray
100
104
  # allocates view state); useful in hot loops where a scatter
101
105
  # result is written back many times.
102
106
  #
103
- # Unlike the arithmetic `scatter_*!` family, `self` may be
104
- # **boolean** (assignment does not widen), and `true` / `false`
105
- # are accepted as scalar `vals` alongside numeric scalars.
107
+ # Unlike the arithmetic `scatter_*!` family, `self` may be of any
108
+ # type (assignment does not widen): **boolean**, with `true` /
109
+ # `false` (or `1` / `0`) as its scalar values, and **object** or
110
+ # **fixlen**, written as `self.flatten[addrs] = vals` writes them
111
+ # (as `put_nd` and `put_along_axis` take them). A scalar `UNDEF`
112
+ # masks every addressed cell.
106
113
  #
107
114
  # @param addrs [CArray, Array<Integer>]
108
- # @param vals [CArray, Numeric, true, false]
115
+ # @param vals [CArray, Object, UNDEF]
109
116
  # @return [self]
110
117
  def scatter_replace!(addrs, vals); end
111
118
 
@@ -14,7 +14,8 @@ class CArray
14
14
  # duration of the block — capturing it across iterations sees the last
15
15
  # slab's data — and it is read-only: writing through it raises. To
16
16
  # produce values per slab use `map_slab` or `reduce_slab`; to write in
17
- # place, assign through the array itself.
17
+ # place, assign through the array itself. Over a Face (`CATime`, a
18
+ # record type) the slab is that Face.
18
19
  # @overload each_slab(axis:)
19
20
  # Returns an Enumerator when no block is given.
20
21
  # @param axis [Integer, Array<Integer>, nil] the slab axis or axes
@@ -29,7 +30,9 @@ class CArray
29
30
  # block's result. The block receives the slab view and must return a
30
31
  # value of the same shape as the slab (a CArray or scalar); the results
31
32
  # are written into the output at the same positions, and a masked
32
- # cell of the block's result is masked in the output.
33
+ # cell of the block's result is masked in the output. Over a Face the
34
+ # slab is that Face, and the output is too unless `data_type:` is
35
+ # given; a read-only Face answers in `:object` cells.
33
36
  # @param axis [Integer, Array<Integer>, nil] the slab axis or axes.
34
37
  # @param data_type [Symbol, Integer, Class, nil] output data type
35
38
  # (defaults to `self`'s data type).
@@ -48,6 +51,10 @@ class CArray
48
51
  # Per-element form (`init:` given): the block receives `(acc, x)` for
49
52
  # each element of the slab and returns the new accumulator; the final
50
53
  # accumulator per slab fills the output.
54
+ #
55
+ # `UNDEF` from the block masks that output cell. Over a Face the slab
56
+ # (and each element) is that Face's, and the output is that Face unless
57
+ # `data_type:` is given; a read-only Face answers in `:object` cells.
51
58
  # @param axis [Integer, Array<Integer>, nil] the slab axis or axes.
52
59
  # @param init [Object] the initial accumulator (selects the
53
60
  # per-element form).
@@ -179,8 +179,8 @@ class CArray
179
179
  # is a one-liner on top of `flatten[axis2addr(...)]`.
180
180
  #
181
181
  # Shape rule: `indices.ndim == self.ndim`, and
182
- # `indices.dim[j] == self.dim[j]` for all `j != axis`;
183
- # `indices.dim[axis]` is free.
182
+ # `indices.shape[j] == self.shape[j]` for all `j != axis`;
183
+ # `indices.shape[axis]` is free.
184
184
  #
185
185
  # `indices` data_type: any integer kind (zero-copy when
186
186
  # already `:int64`). Negative indices: Python-style
@@ -196,9 +196,10 @@ class CArray
196
196
  # @return [CArray] `:int64` view-flat addresses, same shape as
197
197
  # `indices`.
198
198
  # @raise [IndexError] when an index is out of range after
199
- # negative normalization.
200
- # @raise [ArgumentError] for shape / ndim / data_type
201
- # violations.
199
+ # negative normalization, or `indices` is a float or complex
200
+ # array.
201
+ # @raise [ArgumentError] for shape / ndim violations, or a
202
+ # boolean / object `indices`.
202
203
  def axis2addr(indices, axis: 0); end
203
204
 
204
205
  # @!endgroup
@@ -6,8 +6,9 @@ class CArray
6
6
  # @!group Index and address conversion
7
7
 
8
8
  # @overload valid_index?(*idx)
9
- # Returns `true` if the given index tuple is in range for `self`.
10
- # The number of indices must equal `ndim`.
9
+ # Returns `true` if the given index tuple names a cell of `self`,
10
+ # that is, if `self[*idx]` would not raise. A negative index counts
11
+ # from the end of its axis. The number of indices must equal `ndim`.
11
12
  # @param idx [Array<Integer>] one index per axis.
12
13
  # @return [Boolean]
13
14
  # @raise [ArgumentError] if the number of indices does not match
@@ -15,7 +16,8 @@ class CArray
15
16
  def valid_index?(*idx); end
16
17
 
17
18
  # @overload valid_addr?(addr)
18
- # Returns `true` if `addr` is in range `0...elements` for `self`.
19
+ # Returns `true` if `addr` names a cell of `self`: `0...elements`, or
20
+ # a negative address counting from the end (`-elements..-1`).
19
21
  # @param addr [Integer] flat address into the contiguous element
20
22
  # sequence.
21
23
  # @return [Boolean]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carray
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.3
4
+ version: 3.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - himotoyoshi
@@ -111,11 +111,13 @@ files:
111
111
  - ext/carray_attribute.c
112
112
  - ext/carray_bincount.c
113
113
  - ext/carray_broadcast.c
114
+ - ext/carray_build_flags.h
114
115
  - ext/carray_call_cfunc.c
115
116
  - ext/carray_call_cfunc.h
116
117
  - ext/carray_cast.c
117
118
  - ext/carray_cast_func.rb
118
119
  - ext/carray_class.c
120
+ - ext/carray_config.h
119
121
  - ext/carray_conversion.c
120
122
  - ext/carray_copy.c
121
123
  - ext/carray_core.c
@@ -129,6 +131,21 @@ files:
129
131
  - ext/carray_index_classifier.c
130
132
  - ext/carray_index_classifier.h
131
133
  - ext/carray_internal.h
134
+ - ext/carray_kernels_bincmp.c
135
+ - ext/carray_kernels_binop.c
136
+ - ext/carray_kernels_init.c
137
+ - ext/carray_kernels_moncmp.c
138
+ - ext/carray_kernels_monop.c
139
+ - ext/carray_kernels_reduce_aggregate.c
140
+ - ext/carray_kernels_reduce_boolean.c
141
+ - ext/carray_kernels_reduce_cumulative.c
142
+ - ext/carray_kernels_reduce_extreme.c
143
+ - ext/carray_kernels_reduce_variance.c
144
+ - ext/carray_kernels_scan.c
145
+ - ext/carray_kernels_search.c
146
+ - ext/carray_kernels_sort.c
147
+ - ext/carray_kernels_triop.c
148
+ - ext/carray_key_windows.c
132
149
  - ext/carray_lazy.c
133
150
  - ext/carray_loop.c
134
151
  - ext/carray_mask.c
@@ -140,6 +157,7 @@ files:
140
157
  - ext/carray_order.c
141
158
  - ext/carray_partition.c
142
159
  - ext/carray_random.c
160
+ - ext/carray_round.c
143
161
  - ext/carray_scatter.c
144
162
  - ext/carray_segment.c
145
163
  - ext/carray_slab.c
@@ -159,7 +177,6 @@ files:
159
177
  - ext/version.h
160
178
  - ext/version.rb
161
179
  - lib/carray.rb
162
- - lib/carray/arrow_tensor.rb
163
180
  - lib/carray/attribute.rb
164
181
  - lib/carray/autoload_carray.rb
165
182
  - lib/carray/autoload_method_extension.rb
@@ -177,8 +194,10 @@ files:
177
194
  - lib/carray/core_extensions.rb
178
195
  - lib/carray/data_type_extension.rb
179
196
  - lib/carray/data_type_limits.rb
197
+ - lib/carray/distance_limit.rb
180
198
  - lib/carray/fixlen_string.rb
181
199
  - lib/carray/frame.rb
200
+ - lib/carray/frame/column_verbs.rb
182
201
  - lib/carray/frame/concat.rb
183
202
  - lib/carray/frame/convert.rb
184
203
  - lib/carray/frame/csv_parser.rb
@@ -191,11 +210,13 @@ files:
191
210
  - lib/carray/frame/reshape.rb
192
211
  - lib/carray/frame/sort.rb
193
212
  - lib/carray/frame/verbs.rb
213
+ - lib/carray/frame/windows.rb
194
214
  - lib/carray/fuse_source.rb
195
215
  - lib/carray/fusion.rb
196
216
  - lib/carray/histogram.rb
197
217
  - lib/carray/inspect.rb
198
218
  - lib/carray/iterator.rb
219
+ - lib/carray/key_windows.rb
199
220
  - lib/carray/lazy.rb
200
221
  - lib/carray/mask_gap_fill.rb
201
222
  - lib/carray/math.rb
@@ -212,8 +233,10 @@ files:
212
233
  - lib/carray/methods/gather_nd.rb
213
234
  - lib/carray/methods/index.rb
214
235
  - lib/carray/methods/insert_block.rb
236
+ - lib/carray/methods/is_between.rb
215
237
  - lib/carray/methods/is_in.rb
216
238
  - lib/carray/methods/join.rb
239
+ - lib/carray/methods/lag.rb
217
240
  - lib/carray/methods/locate_addr.rb
218
241
  - lib/carray/methods/mask_duplicates.rb
219
242
  - lib/carray/methods/meshgrid.rb
@@ -237,7 +260,6 @@ files:
237
260
  - lib/carray/string_operation_extension.rb
238
261
  - lib/carray/struct.rb
239
262
  - lib/carray/struct_builder.rb
240
- - lib/carray/table.rb
241
263
  - lib/carray/time.rb
242
264
  - lib/carray/window_iterator.rb
243
265
  - yard-stubs/ca_obj_array.rb
@@ -1,401 +0,0 @@
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