carray 3.0.3 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +264 -0
- data/README.md +3 -2
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +1 -6
- data/ext/ca_axis_group.c +27 -27
- data/ext/ca_binop_dispatch.c +1 -29
- data/ext/ca_binop_dispatch.h +0 -1
- data/ext/ca_categorical_iterator.c +205 -134
- data/ext/ca_compare.c +7 -18
- data/ext/ca_group_iter.c +16 -17
- data/ext/ca_kernel_iterator.c +1234 -1994
- data/ext/ca_kernel_iterator.h +391 -437
- data/ext/ca_monop_dispatch.c +82 -59
- data/ext/ca_monop_dispatch.h +1 -0
- data/ext/ca_obj_array.c +96 -39
- data/ext/ca_obj_bincmp.c +24 -21
- data/ext/ca_obj_binop.c +15 -26
- data/ext/ca_obj_bitarray.c +3 -2
- data/ext/ca_obj_bitfield.c +5 -6
- data/ext/ca_obj_block.c +18 -9
- data/ext/ca_obj_byte_swap.c +5 -6
- data/ext/ca_obj_const_string.c +173 -19
- data/ext/ca_obj_face.c +238 -57
- data/ext/ca_obj_face.h +19 -0
- data/ext/ca_obj_fake.c +15 -29
- data/ext/ca_obj_farray.c +6 -3
- data/ext/ca_obj_field.c +17 -5
- data/ext/ca_obj_fixlen_string.c +2 -3
- data/ext/ca_obj_grid.c +406 -132
- data/ext/ca_obj_meld.c +172 -34
- data/ext/ca_obj_moncmp.c +23 -8
- data/ext/ca_obj_monop.c +67 -57
- data/ext/ca_obj_object.c +157 -51
- data/ext/ca_obj_record.c +12 -7
- data/ext/ca_obj_reduce.c +13 -8
- data/ext/ca_obj_refer.c +25 -27
- data/ext/ca_obj_remap.c +30 -9
- data/ext/ca_obj_repeat.c +9 -10
- data/ext/ca_obj_roll.c +6 -5
- data/ext/ca_obj_select.c +14 -9
- data/ext/ca_obj_select_axis.c +15 -47
- data/ext/ca_obj_shift.c +11 -1
- data/ext/ca_obj_stack.c +179 -34
- data/ext/ca_obj_stride.c +162 -115
- data/ext/ca_obj_string.c +71 -4
- data/ext/ca_obj_tile.c +6 -5
- data/ext/ca_obj_time.c +22 -6
- data/ext/ca_obj_timedelta.c +22 -6
- data/ext/ca_obj_transpose.c +5 -3
- data/ext/ca_obj_triop.c +23 -32
- data/ext/ca_obj_window.c +190 -26
- data/ext/ca_sweep_engine.c +2 -2
- data/ext/caframe_csv_reader.c +80 -1
- data/ext/carray.h +181 -75
- data/ext/carray_access.c +184 -96
- data/ext/carray_address_basis.c +343 -87
- data/ext/carray_attribute.c +89 -6
- data/ext/carray_bincount.c +10 -8
- data/ext/carray_broadcast.c +13 -8
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +108 -0
- data/ext/carray_cast.c +142 -24
- data/ext/carray_cast_func.rb +46 -6
- data/ext/carray_class.c +4 -3
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +16 -7
- data/ext/carray_copy.c +51 -14
- data/ext/carray_core.c +147 -55
- data/ext/carray_count.c +50 -20
- data/ext/carray_element.c +76 -33
- data/ext/carray_factorize.c +118 -64
- data/ext/carray_generate.c +191 -50
- data/ext/carray_histogram.c +62 -17
- data/ext/carray_hold.c +12 -15
- data/ext/carray_index_classifier.c +388 -176
- data/ext/carray_internal.h +336 -29
- data/ext/carray_kernels_bincmp.c +4453 -0
- data/ext/carray_kernels_binop.c +11368 -0
- data/ext/carray_kernels_init.c +1734 -0
- data/ext/carray_kernels_moncmp.c +2104 -0
- data/ext/carray_kernels_monop.c +18369 -0
- data/ext/carray_kernels_reduce_aggregate.c +23818 -0
- data/ext/carray_kernels_reduce_boolean.c +406 -0
- data/ext/carray_kernels_reduce_cumulative.c +10086 -0
- data/ext/carray_kernels_reduce_extreme.c +17634 -0
- data/ext/carray_kernels_reduce_variance.c +3849 -0
- data/ext/carray_kernels_scan.c +4302 -0
- data/ext/carray_kernels_search.c +33829 -0
- data/ext/carray_kernels_sort.c +10906 -0
- data/ext/carray_kernels_triop.c +1864 -0
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +170 -111
- data/ext/carray_mask.c +292 -72
- data/ext/carray_median_percentile.c +82 -28
- data/ext/carray_memory_view.c +669 -561
- data/ext/carray_operator.c +124 -47
- data/ext/carray_order.c +384 -76
- data/ext/carray_partition.c +28 -27
- data/ext/carray_random.c +67 -13
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +63 -38
- data/ext/carray_segment.c +3 -3
- data/ext/carray_slab.c +91 -28
- data/ext/carray_sort.c +35 -35
- data/ext/carray_struct.c +19 -18
- data/ext/carray_test.c +26 -5
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +246 -45
- data/ext/mk_call_cfunc.rb +19 -2
- data/ext/mkkernel.rb +198 -240
- data/ext/ruby_carray.c +10 -4
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +126 -22
- data/lib/carray/autoload_carray.rb +8 -31
- data/lib/carray/autoload_method_extension.rb +14 -5
- data/lib/carray/axis_group.rb +255 -106
- data/lib/carray/basics.rb +78 -10
- data/lib/carray/bincount_nd.rb +12 -2
- data/lib/carray/block_iterator.rb +126 -23
- data/lib/carray/boolean_reduce.rb +17 -17
- data/lib/carray/categorical.rb +165 -43
- data/lib/carray/categorical_iterator.rb +130 -27
- data/lib/carray/complex.rb +5 -5
- data/lib/carray/conditional.rb +67 -18
- data/lib/carray/const_string.rb +14 -5
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +7 -1
- data/lib/carray/frame/column_verbs.rb +70 -0
- data/lib/carray/frame/convert.rb +69 -0
- data/lib/carray/frame/csv_parser.rb +111 -17
- data/lib/carray/frame/describe.rb +3 -2
- data/lib/carray/frame/frame.rb +246 -21
- data/lib/carray/frame/group.rb +510 -95
- data/lib/carray/frame/io.rb +115 -28
- data/lib/carray/frame/join.rb +312 -51
- data/lib/carray/frame/records.rb +83 -7
- data/lib/carray/frame/reshape.rb +42 -35
- data/lib/carray/frame/sort.rb +7 -19
- data/lib/carray/frame/verbs.rb +188 -48
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +11 -4
- data/lib/carray/fusion.rb +40 -16
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +31 -8
- data/lib/carray/iterator.rb +85 -7
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +182 -183
- data/lib/carray/mask_gap_fill.rb +63 -57
- data/lib/carray/meld_reduce.rb +12 -14
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +16 -0
- data/lib/carray/methods/bit_string.rb +1 -1
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +3 -1
- data/lib/carray/methods/gather_nd.rb +14 -5
- data/lib/carray/methods/insert_block.rb +2 -2
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +53 -36
- data/lib/carray/methods/join.rb +1 -6
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +47 -51
- data/lib/carray/methods/mode.rb +1 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/resize.rb +3 -3
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +3 -3
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +53 -8
- data/lib/carray/segment_iterator.rb +313 -82
- data/lib/carray/serialize.rb +24 -10
- data/lib/carray/slab_iterator.rb +50 -0
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +8 -1
- data/lib/carray/struct.rb +9 -21
- data/lib/carray/struct_builder.rb +19 -8
- data/lib/carray/time.rb +726 -104
- data/lib/carray/window_iterator.rb +122 -16
- data/lib/carray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +4 -0
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +2 -1
- data/yard-stubs/ca_obj_roll.rb +2 -2
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +8 -3
- data/yard-stubs/carray_access.rb +6 -1
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_cast.rb +10 -0
- data/yard-stubs/carray_conversion.rb +2 -1
- data/yard-stubs/carray_copy.rb +4 -2
- data/yard-stubs/carray_count.rb +4 -4
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_mask.rb +26 -2
- data/yard-stubs/carray_math.rb +27 -1
- data/yard-stubs/carray_median_percentile.rb +5 -2
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +29 -18
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +14 -9
- data/yard-stubs/carray_slab.rb +9 -2
- data/yard-stubs/carray_sort.rb +6 -5
- data/yard-stubs/carray_test.rb +5 -3
- metadata +25 -3
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
|
@@ -63,11 +63,13 @@ require "carray"
|
|
|
63
63
|
require "carray/segment_iterator"
|
|
64
64
|
|
|
65
65
|
# A CAIterator over the categories of a CACategorical. It is a
|
|
66
|
-
# CASegmentIterator over a category-sorted copy of the value:
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
# order).
|
|
66
|
+
# CASegmentIterator over a category-sorted copy of the value: the value is
|
|
67
|
+
# gathered through the categorical's cached grouping plan, so each category
|
|
68
|
+
# is one contiguous segment, and every reduction without `axis:` is the
|
|
69
|
+
# segment reduction over that copy (one value per category, in {#labels}
|
|
70
|
+
# order). With few categories and a numeric value, the count / sum / min /
|
|
71
|
+
# max are read from the values by code instead, and the copy is gathered
|
|
72
|
+
# only for the reductions that need it. This class adds the construction, the labels, and the per-fiber
|
|
71
73
|
# form (`axis:`), which classifies each fiber along an axis and keeps the
|
|
72
74
|
# other axes.
|
|
73
75
|
class CACategoricalIterator < CASegmentIterator
|
|
@@ -106,10 +108,9 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
106
108
|
nvalid = counts.sum
|
|
107
109
|
@offsets = cat.reduceat_index # cached segment STARTS (int64[k])
|
|
108
110
|
@bounds = CArray.segment_offsets(lengths: @elements) # every boundary (int64[k+1])
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
|
|
112
|
-
@perm = nvalid > 0 ? cat.sort_addr[0...nvalid] : CArray.int64(0)
|
|
111
|
+
# The group-major source indices (#perm) are taken from the cached
|
|
112
|
+
# sort_addr when first asked for.
|
|
113
|
+
@nvalid = nvalid
|
|
113
114
|
@codes = cat.codes.reshape(cat.elements) # flat codes view (map re-walk / weights)
|
|
114
115
|
# Gather value into category-contiguous order via the cached permutation
|
|
115
116
|
# and materialise (the reduceat kernels read the grouped buffer's raw ptr,
|
|
@@ -118,9 +119,21 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
118
119
|
# rebuilt per column. With no classified cell (empty / all-excluded) there
|
|
119
120
|
# is nothing to gather — an empty index into an empty source is out of
|
|
120
121
|
# range — so build the empty grouped buffer directly.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
#
|
|
123
|
+
# With few categories, a numeric value is instead copied as it lies:
|
|
124
|
+
# the count / sum / min / max read it in one pass by code (scattering
|
|
125
|
+
# into one slot per category beats gathering every cell first while
|
|
126
|
+
# the slots stay in cache), and the grouped copy is gathered from it
|
|
127
|
+
# only when a reduction needs the categories contiguous. Either way
|
|
128
|
+
# the iterator holds the values as they were when it was built.
|
|
129
|
+
flat = value.reshape(value.elements)
|
|
130
|
+
if @k <= SCATTER_CATEGORIES_MAX && kernel_reads?(value.data_type)
|
|
131
|
+
@as_built = flat.copy
|
|
132
|
+
@empty = @as_built[0...0]
|
|
133
|
+
else
|
|
134
|
+
@grouped = nvalid > 0 ? flat[perm].copy : flat[0...0].copy
|
|
135
|
+
@empty = @grouped[0...0] # an empty category is the values' own kind
|
|
136
|
+
end
|
|
124
137
|
else
|
|
125
138
|
# Shape mismatch: only per-fiber axis: dispatch could still work. With a
|
|
126
139
|
# 1-D value there is no fiber structure to broadcast into, so a mismatch
|
|
@@ -148,6 +161,10 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
148
161
|
self
|
|
149
162
|
end
|
|
150
163
|
|
|
164
|
+
# The most categories for which the count / sum / min / max are read by
|
|
165
|
+
# scattering the values by code rather than through the grouped copy.
|
|
166
|
+
SCATTER_CATEGORIES_MAX = 4096
|
|
167
|
+
|
|
151
168
|
# @overload labels
|
|
152
169
|
# Returns the category vocabulary the results are aligned to.
|
|
153
170
|
# @return [Array]
|
|
@@ -299,7 +316,13 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
299
316
|
|
|
300
317
|
# Both are built with the grouping plan, and only on the flat path; the
|
|
301
318
|
# per-fiber form has neither and must not build a segment's.
|
|
319
|
+
# Group-major source indices = the valid prefix of the cached sort_addr.
|
|
320
|
+
# With no classified cell the prefix is empty (and slicing a length-0
|
|
321
|
+
# sort_addr would be out of range), so take the empty permutation directly.
|
|
302
322
|
def perm
|
|
323
|
+
if @nvalid && ! @perm
|
|
324
|
+
@perm = @nvalid > 0 ? @cat.sort_addr[0...@nvalid] : CArray.int64(0)
|
|
325
|
+
end
|
|
303
326
|
@perm
|
|
304
327
|
end
|
|
305
328
|
|
|
@@ -307,6 +330,34 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
307
330
|
@codes
|
|
308
331
|
end
|
|
309
332
|
|
|
333
|
+
# With few categories the grouped copy is gathered from the values as
|
|
334
|
+
# built, the first time a reduction asks for it.
|
|
335
|
+
def grouped
|
|
336
|
+
if @as_built && ! @grouped
|
|
337
|
+
@grouped = @nvalid > 0 ? @as_built[perm].copy : @as_built[0...0].copy
|
|
338
|
+
end
|
|
339
|
+
super
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def kernel_reads? (data_type = (@as_built || grouped).data_type)
|
|
343
|
+
super(data_type)
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# With few categories the count / sum / min / max come from one pass over
|
|
347
|
+
# the values as built, each cell added to the slot of its code; a cell
|
|
348
|
+
# whose value or code is masked, or whose code names no category, is in
|
|
349
|
+
# no slot, as it is in no segment of the grouped copy.
|
|
350
|
+
def moments
|
|
351
|
+
return @moments if defined?(@moments)
|
|
352
|
+
return super unless @as_built && kernel_reads?
|
|
353
|
+
counts = CArray.int64(@k)
|
|
354
|
+
sums = CArray.float64(@k)
|
|
355
|
+
mins = CArray.new(@as_built.data_type, [@k])
|
|
356
|
+
maxs = CArray.new(@as_built.data_type, [@k])
|
|
357
|
+
@as_built.__send__(:__fiber_scatter_moments__, @codes, 0, @k, counts, sums, mins, maxs)
|
|
358
|
+
@moments = { count: counts, sum: sums, min: mins, max: maxs }
|
|
359
|
+
end
|
|
360
|
+
|
|
310
361
|
# Axis-aware count of present cells. Counting how many cells fall in a
|
|
311
362
|
# group does not look at what is in them. An integer or float payload goes
|
|
312
363
|
# through the fused moments kernel, which counts in the same pass that
|
|
@@ -315,7 +366,7 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
315
366
|
# counted from the codes and the value's mask, one group at a time.
|
|
316
367
|
def axis_counts (axis, op)
|
|
317
368
|
h = @value
|
|
318
|
-
return axis_moments(axis, op)[:count] if
|
|
369
|
+
return axis_moments(axis, op)[:count] if kernel_reads?(h.data_type)
|
|
319
370
|
axis = checked_axis(axis, op)
|
|
320
371
|
full_c = resolve_axis_codes(@cat.codes, h.shape, axis)
|
|
321
372
|
band = h.shape.dup; band.delete_at(axis)
|
|
@@ -349,7 +400,7 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
349
400
|
# this one thing was the odd choice; a caller who wants a fused kernel's
|
|
350
401
|
# four answers shares them by keeping the result.
|
|
351
402
|
def axis_moments (axis, op)
|
|
352
|
-
return axis_moments_by_core(axis, op) unless
|
|
403
|
+
return axis_moments_by_core(axis, op) unless kernel_reads?(@value.data_type)
|
|
353
404
|
h = @value
|
|
354
405
|
axis = checked_axis(axis, op)
|
|
355
406
|
band = h.shape.dup; band.delete_at(axis)
|
|
@@ -382,6 +433,7 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
382
433
|
# kernel had all four from one.
|
|
383
434
|
BAND_ONLY_KEYS = { sum: [:sum], mean: [:sum, :count], min: [:min],
|
|
384
435
|
max: [:max], minmax: [:min, :max] }.freeze
|
|
436
|
+
Ractor.make_shareable(BAND_ONLY_KEYS)
|
|
385
437
|
private_constant :BAND_ONLY_KEYS
|
|
386
438
|
|
|
387
439
|
def band_only_moments (h, axis, band, op)
|
|
@@ -419,7 +471,7 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
419
471
|
# Axis-aware mean: sums / counts (float64); empty group cells (count=0) MASKED.
|
|
420
472
|
# Matches flat #mean per fiber.
|
|
421
473
|
def axis_mean (axis)
|
|
422
|
-
return axis_by_core(axis, :mean) unless
|
|
474
|
+
return axis_by_core(axis, :mean) unless kernel_reads?(@value.data_type)
|
|
423
475
|
m = axis_moments(axis, :mean)
|
|
424
476
|
cnt = m[:count]
|
|
425
477
|
out = m[:sum] / cnt.float64 # count 0 -> NaN
|
|
@@ -448,14 +500,13 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
448
500
|
slot_idx = [nil] + [nil] * band.size # placeholder; c fills slot 0
|
|
449
501
|
codes_bad = full_c.has_mask? ? full_c.is_masked : nil
|
|
450
502
|
@k.times do |c|
|
|
451
|
-
h_local = h.copy
|
|
452
503
|
# Boolean of cells that DO belong to group c (with codes present). On
|
|
453
504
|
# any masked codes cell the codes.eq(c) result carries UNDEF, which
|
|
454
505
|
# naturally reads as "not in group c" for our exclusion purpose.
|
|
455
506
|
in_c = full_c.eq(c)
|
|
456
507
|
exclude = in_c.not
|
|
457
508
|
exclude = exclude | codes_bad if codes_bad
|
|
458
|
-
h_local
|
|
509
|
+
h_local = h.mask_where(exclude)
|
|
459
510
|
slice = h_local.__send__(op, axis: axis) # band shape, mask carries n<contract
|
|
460
511
|
slot_idx[0] = c
|
|
461
512
|
out[*slot_idx] = slice
|
|
@@ -472,16 +523,21 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
472
523
|
# taken by index; otherwise its cells are kept by masking the rest of a copy.
|
|
473
524
|
# A read-only Face's answers (a const string's records index a pool of
|
|
474
525
|
# their own) cannot be put together, so they are collected as the surface
|
|
475
|
-
# values they are, as the flat form does.
|
|
526
|
+
# values they are, as the flat form does. Over a 1-D value each group
|
|
527
|
+
# reduces to one cell, kept as a 1-element array so the answers stack.
|
|
476
528
|
def axis_by_core (axis, op, *args)
|
|
477
529
|
h = @value
|
|
478
530
|
axis = checked_axis(axis, op)
|
|
531
|
+
flat = (h.ndim == 1)
|
|
479
532
|
groups = (0...[@k, 1].max).map { |c| group_cells(h, axis, c, @k.zero?) }
|
|
480
|
-
answers = groups.map
|
|
533
|
+
answers = groups.map do |g|
|
|
534
|
+
flat ? g.__send__(op, *args, axis: axis, keep_axis: true)
|
|
535
|
+
: g.__send__(op, *args, axis: axis)
|
|
536
|
+
end
|
|
481
537
|
if answers.first.face? && answers.first.read_only?
|
|
482
538
|
answers = answers.map { |a| CArray.object(*a.shape) { a.to_a } }
|
|
483
539
|
end
|
|
484
|
-
out = CArray.stack(answers, axis: 0).copy
|
|
540
|
+
out = flat ? CArray.concatenate(answers) : CArray.stack(answers, axis: 0).copy
|
|
485
541
|
@k.zero? ? out[0...0, *([nil] * (out.ndim - 1))].copy : out
|
|
486
542
|
end
|
|
487
543
|
|
|
@@ -493,7 +549,9 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
493
549
|
|
|
494
550
|
def group_cells (h, axis, c, none)
|
|
495
551
|
codes = @cat.codes
|
|
496
|
-
|
|
552
|
+
# Classified along the axis, a group is taken by index; the general way,
|
|
553
|
+
# masking the rest of a copy, gives the same cells.
|
|
554
|
+
if codes.shape == [h.shape[axis]] && CArray.__fast_paths_enabled__
|
|
497
555
|
sel = none ? CArray.boolean(codes.elements) { false } : codes.eq(c)
|
|
498
556
|
sel = sel.strip_mask(false) if sel.has_mask?
|
|
499
557
|
idx = [nil] * h.ndim
|
|
@@ -503,9 +561,8 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
503
561
|
full_c = resolve_axis_codes(codes, h.shape, axis)
|
|
504
562
|
exclude = none ? CArray.boolean(*h.shape) { true } : full_c.eq(c).not
|
|
505
563
|
exclude = exclude | full_c.is_masked if full_c.has_mask?
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
local
|
|
564
|
+
# a copy with the rest masked, which a read-only Face also gives
|
|
565
|
+
h.mask_where(exclude)
|
|
509
566
|
end
|
|
510
567
|
end
|
|
511
568
|
private :axis_by_core, :axis_moments_by_core, :group_cells
|
|
@@ -524,13 +581,43 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
524
581
|
if kernel_scans?(op, answer)
|
|
525
582
|
bundle = @k > 0 ? [@cat.codes, @k, [axis]]
|
|
526
583
|
: [CArray.int64(h.shape[axis]).fill(-1), 1, [axis]]
|
|
527
|
-
return h.
|
|
584
|
+
return h.__scan_by_axis_groups__([axis], [bundle], op)
|
|
528
585
|
end
|
|
529
586
|
each_group_along(axis, ->(shape) { blank_like(answer, shape) }) do |g|
|
|
530
587
|
g.public_send(op, axis: axis)
|
|
531
588
|
end
|
|
532
589
|
end
|
|
533
590
|
|
|
591
|
+
# #lag along an axis (case A): each group's positions along the axis, every
|
|
592
|
+
# other axis kept, lagged along it, so a lag never reaches another group.
|
|
593
|
+
def axis_lag (n, axis)
|
|
594
|
+
axis = checked_axis(axis, :lag)
|
|
595
|
+
classified_along!(:lag, axis)
|
|
596
|
+
each_group_along(axis, ->(shape) { blank_like(@value, shape) }) do |g|
|
|
597
|
+
g.lag(n, axis: axis)
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
# #strip_mask(method:) along an axis (case A): each group's positions along
|
|
602
|
+
# the axis filled along it, fiber by fiber.
|
|
603
|
+
def axis_hold (method, axis)
|
|
604
|
+
axis = checked_axis(axis, :strip_mask)
|
|
605
|
+
classified_along!(:strip_mask, axis)
|
|
606
|
+
each_group_along(axis, ->(shape) { blank_like(@value, shape) }) do |g|
|
|
607
|
+
g.strip_mask(method: method, axis: axis)
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
# #order along an axis (case A): each group's positions along the axis
|
|
612
|
+
# ranked along it, fiber by fiber.
|
|
613
|
+
def axis_order (axis, **kw)
|
|
614
|
+
axis = checked_axis(axis, :order)
|
|
615
|
+
classified_along!(:order, axis)
|
|
616
|
+
each_group_along(axis, ->(shape) { CArray.int64(*shape) }) do |g|
|
|
617
|
+
g.order(axis: axis, **kw)
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
|
|
534
621
|
# #map along an axis (case A): the block receives each group's positions
|
|
535
622
|
# along the axis, every other axis kept, and its answer goes back to them.
|
|
536
623
|
def axis_map (axis, data_type, &block)
|
|
@@ -566,7 +653,7 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
566
653
|
"positions along axis #{axis} (shape [#{n}]); got shape " \
|
|
567
654
|
"#{@cat.codes.shape.inspect}"
|
|
568
655
|
end
|
|
569
|
-
private :axis_scan, :axis_map, :each_group_along, :classified_along!
|
|
656
|
+
private :axis_scan, :axis_lag, :axis_order, :axis_hold, :axis_map, :each_group_along, :classified_along!
|
|
570
657
|
|
|
571
658
|
# Axis-aware wsum + wmean fused (single kernel call, both outputs). Returns
|
|
572
659
|
# [wsum_ca, wmean_ca]. Weights must match source shape exactly (explicit
|
|
@@ -582,6 +669,7 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
582
669
|
"must equal source.shape #{h.shape.inspect}. Wrap 1-D / band-shape " \
|
|
583
670
|
"weights via `.broadcast_to(*source.shape)` before passing."
|
|
584
671
|
end
|
|
672
|
+
return weighted_by_core(weights, axis) unless kernel_reads?(h.data_type)
|
|
585
673
|
codes_h_shape = resolve_axis_codes(@cat.codes, h.shape, axis)
|
|
586
674
|
weights_f64 = weights.data_type == CA_FLOAT64 ? weights : weights.float64
|
|
587
675
|
band = h.shape.dup; band.delete_at(axis)
|
|
@@ -592,10 +680,25 @@ class CACategoricalIterator < CASegmentIterator
|
|
|
592
680
|
[ws_out, wm_out]
|
|
593
681
|
end
|
|
594
682
|
|
|
683
|
+
# wsum and wmean of each group by the core: the group's cells of the value
|
|
684
|
+
# and of the weights, each other cell masked, reduced along the axis.
|
|
685
|
+
def weighted_by_core (weights, axis)
|
|
686
|
+
h = @value
|
|
687
|
+
%i[wsum wmean].map do |op|
|
|
688
|
+
answers = (0...[@k, 1].max).map do |c|
|
|
689
|
+
group_cells(h, axis, c, @k.zero?)
|
|
690
|
+
.public_send(op, group_cells(weights, axis, c, @k.zero?), axis: axis)
|
|
691
|
+
end
|
|
692
|
+
out = CArray.stack(answers, axis: 0).copy
|
|
693
|
+
@k.zero? ? out[0...0, *([nil] * (out.ndim - 1))].copy : out
|
|
694
|
+
end
|
|
695
|
+
end
|
|
696
|
+
private :weighted_by_core
|
|
697
|
+
|
|
595
698
|
# Axis-aware prod: dedicated kernel (identity 1.0, separate from moments to
|
|
596
699
|
# avoid conflating with sum's zero-identity memset).
|
|
597
700
|
def axis_prod (axis)
|
|
598
|
-
return axis_by_core(axis, :prod) unless
|
|
701
|
+
return axis_by_core(axis, :prod) unless kernel_reads?(@value.data_type)
|
|
599
702
|
h = @value
|
|
600
703
|
axis = checked_axis(axis, :prod)
|
|
601
704
|
codes_h_shape = resolve_axis_codes(@cat.codes, h.shape, axis)
|
data/lib/carray/complex.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Complex-component accessors (real / real= / imag / imag= / real? /
|
|
2
|
-
# is_real). Loaded eagerly BEFORE carray/lazy.rb, because lazy.rb
|
|
3
|
-
# `
|
|
4
|
-
#
|
|
2
|
+
# is_real). Loaded eagerly BEFORE carray/lazy.rb, because lazy.rb takes
|
|
3
|
+
# `instance_method(:real)` (and the imag equivalent) at load time -- these
|
|
4
|
+
# defs must exist by then.
|
|
5
5
|
|
|
6
6
|
class CArray
|
|
7
7
|
|
|
@@ -40,9 +40,9 @@ class CArray
|
|
|
40
40
|
# Architecturally:
|
|
41
41
|
# - lib/carray/lazy.rb's `real`/`imag` overrides dispatch to CAMonOp
|
|
42
42
|
# when self is a lazy view, and fall through to the defs here for eager
|
|
43
|
-
# parents (via `
|
|
43
|
+
# parents (via `instance_method(:real)` taken at load time).
|
|
44
44
|
# This file MUST be required before carray/lazy.rb (see carray.rb), and
|
|
45
|
-
# removing these defs would break
|
|
45
|
+
# removing these defs would break the lazy.rb load.
|
|
46
46
|
#
|
|
47
47
|
# - ext/ca_obj_stride.c recognises `CAField .real/.imag over complex`
|
|
48
48
|
# as a first-class byte-mismatched reinterpret pattern. Architectural
|
data/lib/carray/conditional.rb
CHANGED
|
@@ -28,13 +28,18 @@ class CArray
|
|
|
28
28
|
# The result's data type is the common type of the two branches; a
|
|
29
29
|
# branch given as `UNDEF` masks its cells and leaves the type to the
|
|
30
30
|
# other branch (`cond.then_else(a, UNDEF)` has `a`'s data type).
|
|
31
|
+
# String branches make the result a {CAString}
|
|
32
|
+
# (`cond.then_else("yes", "no")`, also with `UNDEF` for one of them).
|
|
33
|
+
# A Symbol branch, or a String beside a value of another kind, makes
|
|
34
|
+
# the result an object array.
|
|
31
35
|
# When `self` or a branch is a lazy view, the result is a lazy view
|
|
32
36
|
# (read-only) that joins the expression around it; `copy` makes an
|
|
33
37
|
# array of it. An object result is always computed at once.
|
|
34
38
|
# @param x [CArray, Numeric, Object] true-branch value(s).
|
|
35
39
|
# @param y [CArray, Numeric, Object] false-branch value(s).
|
|
36
|
-
# @return [CArray] new array with the same shape as `self
|
|
37
|
-
# lazy view (CATriOp) when an
|
|
40
|
+
# @return [CArray] new array with the same shape as `self` (a
|
|
41
|
+
# {CAString} for String branches), or a lazy view (CATriOp) when an
|
|
42
|
+
# operand is lazy.
|
|
38
43
|
# @raise [ArgumentError] when `self` is not a boolean CArray, or when
|
|
39
44
|
# an array branch's shape does not pair with `self`'s as an
|
|
40
45
|
# operator's operands would (equal, or size-1 axes of the same ndim;
|
|
@@ -52,15 +57,10 @@ class CArray
|
|
|
52
57
|
face = [x, y].find { |v| v.is_a?(CArray) && !v.scalar? && v.face? }
|
|
53
58
|
if face
|
|
54
59
|
refuse_branch_shape(x, y) if reshaped
|
|
60
|
+
refuse_branch_mix(x, y, face)
|
|
55
61
|
return face_then_else(x, y, face)
|
|
56
62
|
end
|
|
57
|
-
|
|
58
|
-
# lets a scalar branch take the type of an array branch as an operator
|
|
59
|
-
# does (cond.then_else(0, int32_array) is int32). UNDEF is a missing
|
|
60
|
-
# value, not a value of some type: a branch that is UNDEF leaves the
|
|
61
|
-
# type to the other.
|
|
62
|
-
typed = [x, y].reject { |v| v.equal?(UNDEF) }
|
|
63
|
-
dt = typed.empty? ? CA_OBJECT : CArray.result_type(*typed)
|
|
63
|
+
dt = CArray.send(:selection_type, [x, y])
|
|
64
64
|
return lazy_then_else(x, y, dt) if lazy_then_else?(x, y, dt)
|
|
65
65
|
# A branch shaped unlike the condition goes through the lazy select,
|
|
66
66
|
# which pairs the shapes by the operator rule (equal, or size-1 axes
|
|
@@ -84,7 +84,7 @@ class CArray
|
|
|
84
84
|
if self.has_mask?
|
|
85
85
|
result[self.is_masked] = UNDEF
|
|
86
86
|
end
|
|
87
|
-
result
|
|
87
|
+
CArray.send(:selection_text?, [x, y]) ? CAString.wrap(result) : result
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
# A lazy receiver or branch makes the selection a lazy node (CATriOp
|
|
@@ -93,7 +93,7 @@ class CArray
|
|
|
93
93
|
# a CScalar of it, UNDEF a masked one.
|
|
94
94
|
private def lazy_then_else? (x, y, dt)
|
|
95
95
|
return false if scalar? || dt == CA_OBJECT || dt == CA_FIXLEN
|
|
96
|
-
[self, x, y].any? { |v| v.is_a?(CArray) && v.
|
|
96
|
+
[self, x, y].any? { |v| v.is_a?(CArray) && v.lazy_view? }
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
# The types the lazy select cannot carry keep the strict form: a full
|
|
@@ -104,6 +104,22 @@ class CArray
|
|
|
104
104
|
"then_else: branch shape #{bad.shape.inspect} does not match the condition's #{shape.inspect}"
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
+
# An array branch beside a Face branch must be a Face of the same kind
|
|
108
|
+
# (the same obj_type, so a CARecord subclass and a CARecord of one data
|
|
109
|
+
# class pair, as their store takes): the Face decides what a cell means,
|
|
110
|
+
# so a plain array would be read as its storage (an integer as a time's
|
|
111
|
+
# ticks) and the result would take the class of whichever branch came
|
|
112
|
+
# second. A scalar branch is a value and goes through the Face's store.
|
|
113
|
+
private def refuse_branch_mix (x, y, face)
|
|
114
|
+
arrays = [x, y].select { |v| v.is_a?(CArray) && !v.scalar? }
|
|
115
|
+
return if arrays.all? { |v| v.face? && v.obj_type == face.obj_type }
|
|
116
|
+
hint = face.is_a?(CAString) ?
|
|
117
|
+
" (CArray.string(a) makes a CAString of an object array of Strings)" : ""
|
|
118
|
+
raise ArgumentError,
|
|
119
|
+
"then_else: cannot mix Face and non-Face arrays, or arrays of " \
|
|
120
|
+
"different Face classes; convert them first to one Face class or to no Face" + hint
|
|
121
|
+
end
|
|
122
|
+
|
|
107
123
|
private def lazy_then_else (x, y, dt)
|
|
108
124
|
branch = lambda do |v|
|
|
109
125
|
next v if v.is_a?(CArray) && !v.scalar?
|
|
@@ -178,7 +194,9 @@ class CArray
|
|
|
178
194
|
# Scalar returns from a callable (e.g. `->(v) { 0 }`) broadcast
|
|
179
195
|
# to the subset shape. The result `data_type` is the promotion
|
|
180
196
|
# of the two subset results via `CArray.result_type`, or
|
|
181
|
-
# `data_type` when given.
|
|
197
|
+
# `data_type` when given. A String result is a value (not the name
|
|
198
|
+
# of a data type); when both callables give Strings or {CAString}s
|
|
199
|
+
# the result is a {CAString}. Masked cells in `cond` propagate to
|
|
182
200
|
# `UNDEF` in the result.
|
|
183
201
|
#
|
|
184
202
|
# @param cond [CArray] boolean selector; same shape as `self`.
|
|
@@ -205,18 +223,19 @@ class CArray
|
|
|
205
223
|
x_else = self[cond.not]
|
|
206
224
|
y_then = then_fn.call(x_then)
|
|
207
225
|
y_else = else_fn.call(x_else)
|
|
226
|
+
text = !data_type && CArray.send(:selection_text?, [y_then, y_else])
|
|
208
227
|
|
|
209
228
|
# A callable that returns a scalar (e.g. `->(v) { 0 }`) broadcasts
|
|
210
229
|
# to the subset shape; wrap it here so the scatter step below sees a
|
|
211
230
|
# same-length CArray.
|
|
212
231
|
unless y_then.is_a?(CArray)
|
|
213
|
-
y_then = CArray.new(data_type || CArray.
|
|
232
|
+
y_then = CArray.new(data_type || CArray.send(:selection_type, [y_then]), x_then.shape).fill(y_then)
|
|
214
233
|
end
|
|
215
234
|
unless y_else.is_a?(CArray)
|
|
216
|
-
y_else = CArray.new(data_type || CArray.
|
|
235
|
+
y_else = CArray.new(data_type || CArray.send(:selection_type, [y_else]), x_else.shape).fill(y_else)
|
|
217
236
|
end
|
|
218
237
|
|
|
219
|
-
dt = data_type || CArray.
|
|
238
|
+
dt = data_type || CArray.send(:selection_type, [y_then, y_else])
|
|
220
239
|
out = CArray.new(dt, self.shape)
|
|
221
240
|
out[cond] = y_then
|
|
222
241
|
out[cond.not] = y_else
|
|
@@ -225,7 +244,7 @@ class CArray
|
|
|
225
244
|
# positions, so both scatters leave the cell untouched — an explicit
|
|
226
245
|
# fix-up is required.
|
|
227
246
|
out[cond.is_masked] = UNDEF if cond.has_mask?
|
|
228
|
-
out
|
|
247
|
+
text ? CAString.wrap(out) : out
|
|
229
248
|
end
|
|
230
249
|
|
|
231
250
|
# @overload select(condlist, choicelist, default: 0, data_type: nil)
|
|
@@ -238,7 +257,9 @@ class CArray
|
|
|
238
257
|
# Each `choicelist[k]` is either a same-shape CArray or a scalar
|
|
239
258
|
# broadcast to every cell. The result `data_type` is the promotion
|
|
240
259
|
# of every choice plus `default` via `CArray.result_type`, or
|
|
241
|
-
# `data_type` when given.
|
|
260
|
+
# `data_type` when given. A String choice is a value (not the name of
|
|
261
|
+
# a data type); when every choice and `default` is a String or a
|
|
262
|
+
# {CAString}, the result is a {CAString}.
|
|
242
263
|
#
|
|
243
264
|
# @param condlist [Array<CArray>] boolean selectors.
|
|
244
265
|
# @param choicelist [Array<CArray, Numeric, Object>] values, one
|
|
@@ -274,7 +295,7 @@ class CArray
|
|
|
274
295
|
end
|
|
275
296
|
shape = first.shape
|
|
276
297
|
|
|
277
|
-
dt = data_type ||
|
|
298
|
+
dt = data_type || selection_type(choicelist + [default])
|
|
278
299
|
# `default` can be either a same-shape CArray (per-cell fallback) or a
|
|
279
300
|
# scalar (broadcast to every cell).
|
|
280
301
|
default_full = default.is_a?(CArray) && !default.scalar?
|
|
@@ -297,7 +318,35 @@ class CArray
|
|
|
297
318
|
v = choicelist[k]
|
|
298
319
|
out[c] = v.is_a?(CArray) ? v[c] : v
|
|
299
320
|
end
|
|
321
|
+
if !data_type && selection_text?(choicelist + [default])
|
|
322
|
+
out = CAString.wrap(out)
|
|
323
|
+
end
|
|
300
324
|
out
|
|
301
325
|
end
|
|
302
326
|
|
|
327
|
+
# The data type a selection among `values` writes into: their common type,
|
|
328
|
+
# by the rule an operator promotes its operands by (a scalar takes the
|
|
329
|
+
# type of an array beside it). UNDEF is a missing value, not a value of
|
|
330
|
+
# some type, so it leaves the type to the others. A String or Symbol is a
|
|
331
|
+
# value, which CArray.result_type would read as the name of a data type
|
|
332
|
+
# ("int32"), so it makes the result object.
|
|
333
|
+
def self.selection_type (values)
|
|
334
|
+
typed = values.reject { |v| v.equal?(UNDEF) }
|
|
335
|
+
if typed.empty? || typed.any? { |v| v.is_a?(String) || v.is_a?(Symbol) }
|
|
336
|
+
CA_OBJECT
|
|
337
|
+
else
|
|
338
|
+
CArray.result_type(*typed)
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
private_class_method :selection_type
|
|
342
|
+
|
|
343
|
+
# Whether a selection among `values` gives text: every value that is not
|
|
344
|
+
# UNDEF is a String or a CAString, and there is at least one. The result
|
|
345
|
+
# is then a CAString rather than an object array of Strings.
|
|
346
|
+
def self.selection_text? (values)
|
|
347
|
+
present = values.reject { |v| v.equal?(UNDEF) }
|
|
348
|
+
!present.empty? && present.all? { |v| v.is_a?(String) || v.is_a?(CAString) }
|
|
349
|
+
end
|
|
350
|
+
private_class_method :selection_text?
|
|
351
|
+
|
|
303
352
|
end
|
data/lib/carray/const_string.rb
CHANGED
|
@@ -55,7 +55,7 @@ class CArray
|
|
|
55
55
|
# @return [CAConstString]
|
|
56
56
|
def self.const_string (arg, encoding: Encoding::UTF_8, &block)
|
|
57
57
|
if arg.is_a?(CArray)
|
|
58
|
-
return string_face_of(arg).to_const_string(encoding: encoding)
|
|
58
|
+
return string_face_of(arg, "CArray.const_string").to_const_string(encoding: encoding)
|
|
59
59
|
end
|
|
60
60
|
if block
|
|
61
61
|
n = Integer(arg)
|
|
@@ -135,7 +135,7 @@ class CAConstString
|
|
|
135
135
|
def sort_addr (*args, **kw)
|
|
136
136
|
# The native scan has no notion of an incomparable sentinel, so a masked
|
|
137
137
|
# column goes the same way a per-axis one does.
|
|
138
|
-
return
|
|
138
|
+
return sort_addr_by_bytes if args.empty? && kw.empty? && ! has_mask?
|
|
139
139
|
to_string.sort_addr(*args, **kw)
|
|
140
140
|
end
|
|
141
141
|
|
|
@@ -143,6 +143,12 @@ class CAConstString
|
|
|
143
143
|
# Returns the per-fiber indices that index a sort by string order.
|
|
144
144
|
# @return [CArray] `:int64` indices.
|
|
145
145
|
def sort_index (*args, **kw)
|
|
146
|
+
if args.empty? && kw.empty? && ! has_mask?
|
|
147
|
+
# With no axis the indices are those of the flattened array, which are
|
|
148
|
+
# its addresses.
|
|
149
|
+
addr = sort_addr_by_bytes
|
|
150
|
+
return ndim == 1 ? addr : addr.flatten.copy
|
|
151
|
+
end
|
|
146
152
|
to_string.sort_index(*args, **kw)
|
|
147
153
|
end
|
|
148
154
|
|
|
@@ -163,7 +169,7 @@ class CAConstString
|
|
|
163
169
|
# every cell is masked or the array is empty.
|
|
164
170
|
# @return [String, CAConstString]
|
|
165
171
|
def min (*args, **kw)
|
|
166
|
-
return
|
|
172
|
+
return min_string_by_bytes || UNDEF if args.empty? && kw.empty?
|
|
167
173
|
const_string_lift(to_string.min(*args, **kw))
|
|
168
174
|
end
|
|
169
175
|
|
|
@@ -171,7 +177,7 @@ class CAConstString
|
|
|
171
177
|
# Byte-largest counterpart of {#min}.
|
|
172
178
|
# @return [String, CAConstString]
|
|
173
179
|
def max (*args, **kw)
|
|
174
|
-
return
|
|
180
|
+
return max_string_by_bytes || UNDEF if args.empty? && kw.empty?
|
|
175
181
|
const_string_lift(to_string.max(*args, **kw))
|
|
176
182
|
end
|
|
177
183
|
|
|
@@ -319,6 +325,9 @@ class CAConstString
|
|
|
319
325
|
# @overload categorize(labels: nil, sort_labels: false)
|
|
320
326
|
# @return [CACategorical] categories keyed on the strings.
|
|
321
327
|
def categorize (labels: nil, sort_labels: false)
|
|
328
|
+
# Discovery reads the bytes (a String per distinct value only); a given
|
|
329
|
+
# vocabulary is matched through the strings.
|
|
330
|
+
return super if labels.nil?
|
|
322
331
|
to_string.categorize(labels: labels, sort_labels: sort_labels)
|
|
323
332
|
end
|
|
324
333
|
|
|
@@ -340,7 +349,7 @@ class CAConstString
|
|
|
340
349
|
# @!method ge(other)
|
|
341
350
|
# @return [CArray] boolean, true where self sorts after or equals `other`.
|
|
342
351
|
[:ne, :lt, :le, :gt, :ge].each do |op|
|
|
343
|
-
define_method(op
|
|
352
|
+
define_method(op, &Ractor.make_shareable(proc { |other| to_string.public_send(op, string_operand(other)) }))
|
|
344
353
|
end
|
|
345
354
|
|
|
346
355
|
alias < lt
|
data/lib/carray/construct.rb
CHANGED
|
@@ -287,7 +287,7 @@ class CArray
|
|
|
287
287
|
"(its contents are left undefined); use CArray.new to fill."
|
|
288
288
|
end
|
|
289
289
|
if args.size == 2 and args[1].is_a?(Array)
|
|
290
|
-
return
|
|
290
|
+
return empty_of_data_type(*args, **options)
|
|
291
291
|
end
|
|
292
292
|
unless options.empty?
|
|
293
293
|
raise ArgumentError,
|
|
@@ -314,32 +314,36 @@ class CArray
|
|
|
314
314
|
CArray.new(self::DataType, shape)
|
|
315
315
|
end
|
|
316
316
|
|
|
317
|
-
# @overload from_memory_view(obj)
|
|
317
|
+
# @overload from_memory_view(obj, mask: nil)
|
|
318
318
|
# Returns a fresh CArray interpreted as this typed subclass's
|
|
319
319
|
# `data_type`, imported by copy from a MemoryView producer.
|
|
320
320
|
# Forwards to
|
|
321
|
-
# `CArray.from_memory_view(obj, data_type: self::DataType)
|
|
322
|
-
#
|
|
321
|
+
# `CArray.from_memory_view(obj, data_type: self::DataType)`. A
|
|
322
|
+
# typed producer keeps its shape and must agree with the type; a
|
|
323
|
+
# typeless one is read as 1-D (chain `.reshape` for higher rank).
|
|
323
324
|
# @param obj [Object] MemoryView producer.
|
|
325
|
+
# @param mask [Object, nil] MemoryView producer of the mask.
|
|
324
326
|
# @return [CArray]
|
|
325
|
-
def from_memory_view (obj)
|
|
326
|
-
CArray.from_memory_view(obj, data_type: self::DataType)
|
|
327
|
+
def from_memory_view (obj, mask: nil)
|
|
328
|
+
CArray.from_memory_view(obj, data_type: self::DataType, mask: mask)
|
|
327
329
|
end
|
|
328
330
|
|
|
329
|
-
# @overload wrap_memory_view(obj)
|
|
331
|
+
# @overload wrap_memory_view(obj, mask: nil)
|
|
330
332
|
# Returns a zero-copy CAWrap over `obj`'s MemoryView interpreted
|
|
331
333
|
# as this typed subclass's `data_type`. Forwards to
|
|
332
|
-
# `CArray.wrap_memory_view(obj, data_type: self::DataType)
|
|
333
|
-
#
|
|
334
|
+
# `CArray.wrap_memory_view(obj, data_type: self::DataType)`. A
|
|
335
|
+
# typed producer keeps its shape and must agree with the type; a
|
|
336
|
+
# typeless one is read as 1-D (chain `.reshape` for higher rank).
|
|
334
337
|
#
|
|
335
338
|
# Here the receiver names the element type, not the class of the
|
|
336
339
|
# result: the call forwards with `CArray` as the receiver, so a
|
|
337
340
|
# `CAWrap` comes back. Choosing the class is the other reading of
|
|
338
341
|
# the receiver, and it lives on `CArray.wrap_memory_view` itself.
|
|
339
342
|
# @param obj [Object] MemoryView producer.
|
|
343
|
+
# @param mask [Object, nil] MemoryView producer of the mask.
|
|
340
344
|
# @return [CAWrap]
|
|
341
|
-
def wrap_memory_view (obj)
|
|
342
|
-
CArray.wrap_memory_view(obj, data_type: self::DataType)
|
|
345
|
+
def wrap_memory_view (obj, mask: nil)
|
|
346
|
+
CArray.wrap_memory_view(obj, data_type: self::DataType, mask: mask)
|
|
343
347
|
end
|
|
344
348
|
|
|
345
349
|
end
|
|
@@ -164,6 +164,7 @@ class CArray
|
|
|
164
164
|
# midpoints go the same way as numpy's integer linspace).
|
|
165
165
|
float_out = CArray.new(CA_FLOAT64, [n]).span(x1.to_f..x2.to_f)
|
|
166
166
|
return float_out if data_type == CA_FLOAT64
|
|
167
|
+
# coerce: the type the caller asked for
|
|
167
168
|
float_out.floor.to_type(data_type)
|
|
168
169
|
end
|
|
169
170
|
|