cumo 0.14.1 → 0.15.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0872662f2f59cb4486df50f2f9b6dee5fc62fe938f6c47bafdb1ebdfc744c2f6'
4
- data.tar.gz: 248f4ea1224ee0ff46069d16b424b1c90a98d81b33610506c58d45ffb9693c08
3
+ metadata.gz: d6fcb0462dc2821749f43c243267e8eef32c5362d2a647769c3f93ccc048ddd0
4
+ data.tar.gz: 131ec1045f331591fb3f1c522824ea1e14c56fad40091bccacde73bb7f64a649
5
5
  SHA512:
6
- metadata.gz: caf8d608d673afd9e71d3c421d2c096db503bb0054bf1ca846f33de738ccb2662ab53a6c715e6003f9a27b48d148d1e8f60f509076d53eee1b1bebf56c023e97
7
- data.tar.gz: 78c2fd433b79741516f842b069adfa90c0452694a106554e99fd0ac5415684d6b82beb7f44dae63a09a745fc075bf21c1fb71276fa8ad691965f9ddd335e8d9a
6
+ metadata.gz: dfbb78f6f9ea625ae2848210722aa58106e6efd80af31d38e239585def253b7fb0e00426d18804e4177040492469d3498eaf61dca0cb7880ee482bd1c9669d0e
7
+ data.tar.gz: 80c944b3b2a544c593a221c9600c2a8cc441b273e37dc77f3ca1bbb738e381e1fcc0db42cbc66265f6d75832ee0ca396b008ffec7ff666549db313d386b82e81
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ # 0.15.0 (2026/11/02)
2
+
3
+ Breaking changes:
4
+
5
+ * A subscript of an empty array is read as it is for any other array, where every subscript of an empty array raised `Cumo::NArray::ShapeError`. `a[true, 1]` of a 0 x 3 array answers an empty view, and a subscript that names an element raises as it does for any array, so `a[0]` raises `IndexError`. An empty range now selects nothing when it starts where an element could be inserted, as with `Array#[]`, so `b[2...2, true]` of a 2 x 3 array answers a 0 x 3 view where it raised `RangeError`. The README lists where this differs from Numo (PR #641)
6
+ * `Cumo::RObject` no longer has `store_binary`, `to_string`, `hton` or `Cumo::RObject.from_string`, and `set` with a String raises `NoMethodError` for it. They read or wrote the object references of an RObject as raw bytes, so the next garbage collection could crash, and `to_string` gave out heap addresses. Calling the methods of `Cumo::NArray` on an RObject through `bind_call` raises `TypeError` (PR #638)
7
+
8
+ Fixes:
9
+
10
+ * Fix `require "cumo"` under `ruby -w` warning "method redefined; discarding old %". Cumo no longer defines `Range#%` again, which Ruby has had since 2.6 and which works the same (PR #643)
11
+ * Fix a crash, possible since 0.7.0, when storing an Array whose NArray row of another type is followed by a row that raises, such as one with the wrong number of dimensions. The garbage collector could free the cast copy of the row before the store let go of it (PR #642)
12
+ * Fix a range with a negative step and an excluded or missing end selecting the wrong elements: `(4...1).step(-1)` selected 5 elements of a 5-element array where it should select 3, and `(3..).step(-1)` none. Fix a stepped range that selects nothing returning elements, as `(1..0).step(2)` returned one and `(3...3).step(-1)` two (PR #641)
13
+ * Fix `to_a` and `format_to_a` aborting when Cumo is compiled with `RUBY_DEBUG` against a Ruby whose headers check the index of `RARRAY_AREF`, as Ruby master does since ruby/ruby@61261bf5c4 (PR #640)
14
+
15
+ Changes:
16
+
17
+ * CI builds against CUDA 13.4.3 on Ubuntu 26.04 where it built against 13.4.1 on Ubuntu 24.04, and the CUDA 12.9.2 job builds for sm_121 where it built for sm_120 (PR #639)
18
+ * The first `sort`, `median` or `sort_index` of a type loads the sort kernels of that type alone, where the first of a process loaded those of all twelve types. On SFloat the first `sort` is about 5.7 times faster, `median` about 9 times and `sort_index` about 3 times (PR #636)
19
+ * The first `cumsum` or `cumprod` of a process no longer loads every sort kernel, which makes the first SFloat `cumsum` about 2.1 times faster and keeps the peak RSS 57 MB lower (PR #635)
20
+ * `sum`, `mean`, `max`, `min` and the `dot` of two vectors have a kernel module of their own, so the first of them on a type no longer loads every reduction of the type. The first SFloat `sum` or vector `dot` of a process is about 8.6 times faster (PR #634)
21
+
1
22
  # 0.14.1 (2026/10/22)
2
23
 
3
24
  Fixes:
data/README.md CHANGED
@@ -198,6 +198,30 @@ Cumo::DFloat.new(1, 3).seq.transpose.contiguous? #=> true
198
198
  Code that runs against both libraries should not read `contiguous?` and expect the same answer.
199
199
  Where it wants a contiguous array it can ask for one, since `dup` answers one in either library.
200
200
 
201
+ #### Subscripts That Select Nothing
202
+
203
+ Numo raises `ShapeError` for every subscript of an empty array.
204
+ Cumo returns the empty view numpy does, and raises only for a subscript that names an element.
205
+
206
+ ```ruby
207
+ a = Cumo::DFloat.new(0, 3)
208
+ a[true, 1].shape #=> [0], where Numo raises ShapeError
209
+ a[0] # raises IndexError, where Numo raises ShapeError
210
+ ```
211
+
212
+ A range that selects nothing only has to start where an element could be inserted, as with `Array#[]`.
213
+ Numo raises `RangeError` when such a range starts at either end of an axis.
214
+
215
+ ```ruby
216
+ b = Cumo::DFloat.new(2, 3).seq
217
+ b[2...2, true].shape #=> [0, 3], where Numo raises RangeError
218
+ b[3...3, true] # raises RangeError in both
219
+ ```
220
+
221
+ A stepped range that selects nothing also selects nothing in Cumo.
222
+ Numo returns one element for `(1..0).step(2)` and two for `(3...3).step(-1)`.
223
+ `compatible_mode` does not change any of this.
224
+
201
225
  ### Keeping Scalars On The Device
202
226
 
203
227
  The 0-dimensional return is what lets an iterative loop stay on the GPU.
data/Rakefile CHANGED
@@ -39,7 +39,9 @@ Rake::TestTask.new(:test) do |t|
39
39
  end
40
40
 
41
41
  require "rake/extensiontask"
42
- Rake::ExtensionTask.new("cumo")
42
+ ext = Rake::ExtensionTask.new("cumo")
43
+ # The Makefile lists an object for each kernel part, so a new part needs extconf.rb to run again.
44
+ file "#{ext.tmp_dir}/#{ext.platform}/#{ext.name}/#{RUBY_VERSION}/Makefile" => "ext/cumo/narray/gen/kernel_parts.rb"
43
45
 
44
46
  desc 'Build the C++ memory pool test harness without running it (running it needs a GPU)'
45
47
  task :build_ctest do
@@ -0,0 +1,26 @@
1
+ #include <cub/util_device.cuh>
2
+ #include "cumo/template_kernel.h"
3
+
4
+ namespace {
5
+
6
+ __global__ void cumo_cub_ptx_version_probe() {}
7
+
8
+ } // namespace
9
+
10
+ // CUB learns each device's PTX version once, from the attributes of an empty
11
+ // kernel. Each translation unit has its own empty kernel, but the link keeps
12
+ // one copy of the code that asks. The query loads the module that copy came
13
+ // from, which can be a large one the caller never runs. This module has one
14
+ // kernel built with the same flags, so it gives the same version at little cost.
15
+ extern "C" void
16
+ cumo_cuda_cub_prime_ptx_version(void)
17
+ {
18
+ cub::GetPerDeviceAttributeCache<cub::PtxVersionCacheTag>()(
19
+ [](int& ptx_version) {
20
+ cudaFuncAttributes attrs = {};
21
+ cudaError_t status = cudaFuncGetAttributes(&attrs, (const void*)cumo_cub_ptx_version_probe);
22
+ ptx_version = attrs.ptxVersion * 10;
23
+ return status;
24
+ },
25
+ cub::CurrentDevice());
26
+ }
data/ext/cumo/extconf.rb CHANGED
@@ -55,7 +55,6 @@ narray/ndloop
55
55
  narray/ndloop_kernel
56
56
  narray/data
57
57
  narray/data_kernel
58
- narray/sort_kernel
59
58
  narray/math
60
59
  narray/SFMT
61
60
  narray/struct
@@ -70,6 +69,7 @@ cuda/cudnn
70
69
  cuda/cudnn_impl
71
70
  cuda/cusolver
72
71
  cuda/cufft
72
+ cuda/cub
73
73
  )
74
74
  %w[bit int8 int16 int32 int64 uint8 uint16 uint32 uint64 hfloat bfloat sfloat dfloat scomplex dcomplex robject].each do |type_name|
75
75
  srcs << "narray/types/#{type_name}"
@@ -1,3 +1,6 @@
1
+ #ifndef CUMO_SORT_KERNEL_H
2
+ #define CUMO_SORT_KERNEL_H
3
+
1
4
  #include "cumo/narray_kernel.h"
2
5
  #include "cumo/indexer.h"
3
6
  #include "cumo/template_kernel.h"
@@ -146,6 +149,7 @@ cudaError_t sort_pairs(const Key* kin, Key* kout, const Value* vin, Value* vout,
146
149
  int64_t total, int64_t n_rows, int64_t row_len) {
147
150
  size_t bytes = 0;
148
151
  cudaError_t st = cudaSuccess;
152
+ cumo_cuda_cub_prime_ptx_version();
149
153
  if (n_rows == 1) {
150
154
  st = cub::DeviceRadixSort::SortPairs(nullptr, bytes, kin, kout, vin, vout, total, 0, sizeof(Key) * 8, cumo_cuda_stream());
151
155
  if (st != cudaSuccess) { return st; }
@@ -168,6 +172,7 @@ template <typename Key>
168
172
  cudaError_t sort_keys(const Key* kin, Key* kout, int64_t total, int64_t n_rows, int64_t row_len) {
169
173
  size_t bytes = 0;
170
174
  cudaError_t st = cudaSuccess;
175
+ cumo_cuda_cub_prime_ptx_version();
171
176
  if (n_rows == 1) {
172
177
  st = cub::DeviceRadixSort::SortKeys(nullptr, bytes, kin, kout, total, 0, sizeof(Key) * 8, cumo_cuda_stream());
173
178
  if (st != cudaSuccess) { return st; }
@@ -329,6 +334,12 @@ __global__ void iota_kernel(I* out, int64_t n) {
329
334
  }
330
335
  }
331
336
 
337
+ // The positions go to CUB as this rather than as a bare integer, so that
338
+ // SortPairs is a function of this file alone. With a bare integer it is the
339
+ // same function for every type whose key has the same width, and the link
340
+ // keeps one, so SFloat would launch the kernels of the UInt32 module.
341
+ template <typename I> struct position { I value; };
342
+
332
343
  // perm[i] is the position the i-th smallest element sat at, and the index
333
344
  // array holds the number to answer for that position.
334
345
  template <int NDIM, typename I>
@@ -373,13 +384,13 @@ void sort_index_rows(cumo_na_iarray_t* a, cumo_na_indexer_t* indexer, cumo_na_ia
373
384
  key_t* kout = (key_t*)cumo_cuda_runtime_malloc(sizeof(key_t) * total);
374
385
  float_key_kernel<T><<<grid_dim, block_dim, 0, cumo_cuda_stream()>>>(data, kin, total);
375
386
  cumo_check_launch_holding(kin, kout, pin, pout, gathered);
376
- cumo_check_status_holding(sort_pairs(kin, kout, pin, pout, total, n_rows, row_len),
387
+ cumo_check_status_holding(sort_pairs(kin, kout, (const position<I>*)pin, (position<I>*)pout, total, n_rows, row_len),
377
388
  kin, kout, pin, pout, gathered);
378
389
  cumo_cuda_runtime_free((char*)kout);
379
390
  cumo_cuda_runtime_free((char*)kin);
380
391
  } else {
381
392
  T* kout = (T*)cumo_cuda_runtime_malloc(sizeof(T) * total);
382
- cumo_check_status_holding(sort_pairs(data, kout, pin, pout, total, n_rows, row_len),
393
+ cumo_check_status_holding(sort_pairs(data, kout, (const position<I>*)pin, (position<I>*)pout, total, n_rows, row_len),
383
394
  kout, pin, pout, gathered);
384
395
  cumo_cuda_runtime_free((char*)kout);
385
396
  }
@@ -396,7 +407,7 @@ void sort_index_rows(cumo_na_iarray_t* a, cumo_na_indexer_t* indexer, cumo_na_ia
396
407
 
397
408
  } // namespace
398
409
 
399
- // float_key is only defined for the two float types, so the integer entry
410
+ // float_key is only defined for the float types, so the integer entry
400
411
  // points must not instantiate it; the bool picks the branch at compile time.
401
412
  #define CUMO_DEF_SORT(name, type, is_float) \
402
413
  extern "C" void cumo_##name##_sort_kernel_launch( \
@@ -420,15 +431,4 @@ void sort_index_rows(cumo_na_iarray_t* a, cumo_na_indexer_t* indexer, cumo_na_ia
420
431
  } \
421
432
  }
422
433
 
423
- CUMO_DEF_SORT(int8, int8_t, false)
424
- CUMO_DEF_SORT(int16, int16_t, false)
425
- CUMO_DEF_SORT(int32, int32_t, false)
426
- CUMO_DEF_SORT(int64, int64_t, false)
427
- CUMO_DEF_SORT(uint8, u_int8_t, false)
428
- CUMO_DEF_SORT(uint16, u_int16_t, false)
429
- CUMO_DEF_SORT(uint32, u_int32_t, false)
430
- CUMO_DEF_SORT(uint64, u_int64_t, false)
431
- CUMO_DEF_SORT(hfloat, cumo_half, true)
432
- CUMO_DEF_SORT(bfloat, cumo_bfloat, true)
433
- CUMO_DEF_SORT(sfloat, float, true)
434
- CUMO_DEF_SORT(dfloat, double, true)
434
+ #endif /* ifndef CUMO_SORT_KERNEL_H */
@@ -186,6 +186,8 @@ void cumo_cuda_runtime_check_kernel_launch(void);
186
186
  // The stream every launch and copy goes to. It is 0 until Stream#with says
187
187
  // otherwise, and thread local, so one thread's choice is not another's.
188
188
  struct CUstream_st* cumo_cuda_stream(void);
189
+ // Call before any CUB or Thrust algorithm. Defined in cuda/cub.cu, which says why.
190
+ void cumo_cuda_cub_prime_ptx_version(void);
189
191
 
190
192
  // The forms to call while scratch is held, which free what they are given
191
193
  // before they raise. Pass every buffer that is still outstanding, including the
@@ -158,6 +158,9 @@ cumo_na_swap_byte(VALUE self)
158
158
  CUMO_STRIDE_LOOP|CUMO_NDF_INDEXER_LOOP|CUMO_NDF_ACCEPT_BYTESWAP,
159
159
  1, 1, ain, aout };
160
160
 
161
+ if (rb_obj_is_kind_of(self, cumo_cRObject)) {
162
+ rb_raise(rb_eTypeError, "cannot swap bytes of %s", rb_obj_classname(self));
163
+ }
161
164
  // Cumo::Bit's element is one bit, so swapping the bytes of an element
162
165
  // changes nothing, and a loop stepping by the element stride would walk
163
166
  // one byte per bit, eight times past the end of the packed buffer. Only
@@ -41,14 +41,14 @@ erb_dir.unshift("tmpl_bit") if (type_name == "bit")
41
41
  erb_dir.map! { |d| File.join(thisdir, d) }
42
42
 
43
43
  TEMPLATE_KERNEL_PARTS = {
44
- "accum" => %w[reduce reduce_nan],
45
- "accum_index" => %w[reduce reduce_nan],
46
- "accum_arg" => %w[reduce reduce_nan],
47
- "accum_binary" => %w[reduce reduce_nan reduce_from],
48
- "cum" => %w[reduce],
49
- "layer_norm" => %w[reduce],
50
- "rms_norm" => %w[reduce],
51
- "softmax" => %w[reduce],
44
+ "accum" => %w[reduce reduce_rare reduce_nan],
45
+ "accum_index" => %w[reduce reduce_rare reduce_nan],
46
+ "accum_arg" => %w[reduce reduce_rare reduce_nan],
47
+ "accum_binary" => %w[reduce reduce_rare reduce_nan reduce_from],
48
+ "cum" => %w[reduce_rare],
49
+ "layer_norm" => %w[reduce_rare],
50
+ "rms_norm" => %w[reduce_rare],
51
+ "softmax" => %w[reduce_rare],
52
52
  "bit_count" => %w[reduce],
53
53
  "bit_reduce" => %w[reduce],
54
54
  "bit_stat" => %w[reduce],
@@ -59,10 +59,14 @@ TEMPLATE_KERNEL_PARTS = {
59
59
  "poly" => %w[misc],
60
60
  "quantize_symmetric" => %w[misc],
61
61
  "unary_ret2" => %w[misc],
62
+ "sort" => %w[sort],
62
63
  }.freeze
63
64
 
64
65
  MATH_TEMPLATES = %w[unary_s binary_s ternary_s frexp].freeze
65
66
  COMMON_MATH = %w[sqrt log log2 log10 exp exp2 sin cos tan tanh erf gelu gelu_tanh silu sigmoid softplus].freeze
67
+ # mulsum is here for dot of two vectors.
68
+ COMMON_REDUCE = %w[sum mean max min mulsum].freeze
69
+ RARE_REDUCE = %w[prod ptp minmax var stddev rms abs_sum abs_max abs_min kahan_sum min_index max_index argmin argmax].freeze
66
70
 
67
71
  abort "unknown kernel part: #{$kernel_part}" unless KERNEL_PARTS.include?($kernel_part)
68
72
  undeclared_math = COMMON_MATH - File.read(File.join(thisdir, "spec.rb")).scan(/^\s*math "(\w+)"/).flatten
@@ -83,8 +87,13 @@ class ErbPP
83
87
  @children.select { |c| c.kernel_parts.include?($kernel_part) }
84
88
  end
85
89
 
86
- def kernel_part?(nan: false, from: nil)
87
- $kernel_part == (from ? "reduce_from" : nan ? "reduce_nan" : "reduce")
90
+ def kernel_part?(op = nil, nan: false, from: nil)
91
+ return $kernel_part == "reduce_from" if from
92
+ return $kernel_part == "reduce_nan" if nan
93
+ return $kernel_part == "reduce" if COMMON_REDUCE.include?(op)
94
+ return $kernel_part == "reduce_rare" if RARE_REDUCE.include?(op)
95
+
96
+ raise ArgumentError, "#{op.inspect} is in neither COMMON_REDUCE nor RARE_REDUCE"
88
97
  end
89
98
 
90
99
  def indexer_dims(narrow = false)
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- KERNEL_PARTS = %w[main reduce reduce_nan reduce_from math math_rare compare cast misc].freeze
3
+ KERNEL_PARTS = %w[main reduce reduce_nan reduce_from reduce_rare math math_rare compare cast misc sort].freeze
4
4
  MATH_TYPES = %w[hfloat bfloat sfloat dfloat scomplex dcomplex].freeze
5
+ SORT_TYPES = %w[int8 int16 int32 int64 uint8 uint16 uint32 uint64 hfloat bfloat sfloat dfloat].freeze
5
6
 
6
7
  def kernel_parts_of(type_name)
7
- return KERNEL_PARTS.first(4) if type_name == "robject"
8
+ return %w[main reduce reduce_nan reduce_from] if type_name == "robject"
8
9
 
9
10
  KERNEL_PARTS.reject do |part|
10
11
  (%w[math math_rare].include?(part) && !MATH_TYPES.include?(type_name)) ||
11
- (%w[compare misc].include?(part) && type_name == "bit")
12
+ (%w[compare misc reduce_rare].include?(part) && type_name == "bit") ||
13
+ (part == "sort" && !SORT_TYPES.include?(type_name))
12
14
  end
13
15
  end
14
16
 
@@ -100,8 +100,12 @@ end
100
100
 
101
101
  if is_object
102
102
  undef_singleton_method "from_binary"
103
+ undef_singleton_method "from_string"
104
+ undef_method "store_binary"
103
105
  undef_method "to_binary"
106
+ undef_method "to_string"
104
107
  undef_method "swap_byte"
108
+ undef_method "hton"
105
109
  undef_method "to_network"
106
110
  undef_method "to_vacs"
107
111
  undef_method "to_host"
@@ -119,7 +119,7 @@ extern "C" {
119
119
  #endif
120
120
  #endif
121
121
 
122
- <% if kernel_part? %>
122
+ <% if kernel_part?("argmin") %>
123
123
  void cumo_<%=type_name%>_argmin_int<%=i%>_kernel_launch(cumo_na_reduction_arg_t* arg)
124
124
  {
125
125
  cumo_reduce_arg_split<dtype, idx_t, cumo_<%=type_name%>_argmin_int<%=i%>_impl>(*arg, cumo_<%=type_name%>_argmin_int<%=i%>_impl{});
@@ -133,7 +133,7 @@ void cumo_<%=type_name%>_argmin_nan_int<%=i%>_kernel_launch(cumo_na_reduction_ar
133
133
  }
134
134
  <% end %>
135
135
 
136
- <% if kernel_part? %>
136
+ <% if kernel_part?("argmax") %>
137
137
  void cumo_<%=type_name%>_argmax_int<%=i%>_kernel_launch(cumo_na_reduction_arg_t* arg)
138
138
  {
139
139
  cumo_reduce_arg_split<dtype, idx_t, cumo_<%=type_name%>_argmax_int<%=i%>_impl>(*arg, cumo_<%=type_name%>_argmax_int<%=i%>_impl{});
@@ -63,7 +63,7 @@ extern "C" {
63
63
  //<% (is_float ? ["","_nan"] : [""]).each do |nan| %>
64
64
  //<% ([nil] + from_types).each do |from| %>
65
65
  //<% sfx = from ? "_from_#{from[:id]}" : "" %>
66
- //<% if kernel_part?(nan: nan == "_nan", from: from) %>
66
+ //<% if kernel_part?(name, nan: nan == "_nan", from: from) %>
67
67
  void <%="cumo_#{type_name}_#{name}#{nan}#{sfx}_kernel_launch"%>(cumo_na_reduction_arg_t* arg, cumo_na_iarray_t* in2)
68
68
  {
69
69
  cumo_reduce_zip_split<dtype, <%= from ? from[:ctype] : "dtype" %>, dtype, <%="cumo_#{type_name}_#{name}#{nan}#{sfx}_impl"%>>(*arg, *in2, <%="cumo_#{type_name}_#{name}#{nan}#{sfx}_impl"%>{});
@@ -119,7 +119,7 @@ extern "C" {
119
119
  #endif
120
120
  #endif
121
121
 
122
- <% if kernel_part? %>
122
+ <% if kernel_part?("min_index") %>
123
123
  void cumo_<%=type_name%>_min_index_int<%=i%>_kernel_launch(cumo_na_reduction_arg_t* arg)
124
124
  {
125
125
  cumo_reduce_split<dtype, idx_t, cumo_<%=type_name%>_min_index_int<%=i%>_impl>(*arg, cumo_<%=type_name%>_min_index_int<%=i%>_impl{});
@@ -133,7 +133,7 @@ void cumo_<%=type_name%>_min_index_nan_int<%=i%>_kernel_launch(cumo_na_reduction
133
133
  }
134
134
  <% end %>
135
135
 
136
- <% if kernel_part? %>
136
+ <% if kernel_part?("max_index") %>
137
137
  void cumo_<%=type_name%>_max_index_int<%=i%>_kernel_launch(cumo_na_reduction_arg_t* arg)
138
138
  {
139
139
  cumo_reduce_split<dtype, idx_t, cumo_<%=type_name%>_max_index_int<%=i%>_impl>(*arg, cumo_<%=type_name%>_max_index_int<%=i%>_impl{});
@@ -215,33 +215,43 @@ extern "C" {
215
215
  #endif
216
216
  #endif
217
217
 
218
- <% if kernel_part? %>
218
+ <% if kernel_part?("sum") %>
219
219
  void cumo_<%=type_name%>_sum_kernel_launch(cumo_na_reduction_arg_t* arg)
220
220
  {
221
221
  cumo_reduce_split<dtype, <%=dtype%>, cumo_<%=type_name%>_sum_impl>(*arg, cumo_<%=type_name%>_sum_impl{});
222
222
  }
223
+ <% end %>
223
224
 
225
+ <% if kernel_part?("prod") %>
224
226
  void cumo_<%=type_name%>_prod_kernel_launch(cumo_na_reduction_arg_t* arg)
225
227
  {
226
228
  cumo_reduce_split<dtype, <%=dtype%>, cumo_<%=type_name%>_prod_impl>(*arg, cumo_<%=type_name%>_prod_impl{});
227
229
  }
230
+ <% end %>
228
231
 
232
+ <% if kernel_part?("mean") %>
229
233
  void cumo_<%=type_name%>_mean_kernel_launch(cumo_na_reduction_arg_t* arg)
230
234
  {
231
235
  rtype n = (rtype)(arg->in_indexer.total_size / arg->out_indexer.total_size);
232
236
  cumo_reduce_split<dtype, dtype, cumo_<%=type_name%>_mean_impl>(*arg, cumo_<%=type_name%>_mean_impl{n});
233
237
  }
238
+ <% end %>
234
239
 
240
+ <% if kernel_part?("var") %>
235
241
  void cumo_<%=type_name%>_var_kernel_launch(cumo_na_reduction_arg_t* arg)
236
242
  {
237
243
  cumo_reduce_split<dtype, rtype, cumo_<%=type_name%>_var_impl>(*arg, cumo_<%=type_name%>_var_impl{});
238
244
  }
245
+ <% end %>
239
246
 
247
+ <% if kernel_part?("stddev") %>
240
248
  void cumo_<%=type_name%>_stddev_kernel_launch(cumo_na_reduction_arg_t* arg)
241
249
  {
242
250
  cumo_reduce_split<dtype, rtype, cumo_<%=type_name%>_stddev_impl>(*arg, cumo_<%=type_name%>_stddev_impl{});
243
251
  }
252
+ <% end %>
244
253
 
254
+ <% if kernel_part?("rms") %>
245
255
  void cumo_<%=type_name%>_rms_kernel_launch(cumo_na_reduction_arg_t* arg)
246
256
  {
247
257
  rtype n = (rtype)(arg->in_indexer.total_size / arg->out_indexer.total_size);
@@ -280,25 +290,29 @@ void cumo_<%=type_name%>_rms_nan_kernel_launch(cumo_na_reduction_arg_t* arg)
280
290
  cumo_reduce_split<dtype, rtype, cumo_<%=type_name%>_rms_nan_impl>(*arg, cumo_<%=type_name%>_rms_nan_impl{});
281
291
  }
282
292
  <% end %>
283
- <% if kernel_part? %>
293
+ <% if kernel_part?("abs_sum") %>
284
294
  void cumo_<%=type_name%>_abs_sum_kernel_launch(cumo_na_reduction_arg_t* arg)
285
295
  {
286
296
  cumo_reduce_split<dtype, rtype, cumo_<%=type_name%>_abs_sum_impl>(*arg, cumo_<%=type_name%>_abs_sum_impl{});
287
297
  }
298
+ <% end %>
288
299
 
300
+ <% if kernel_part?("abs_max") %>
289
301
  void cumo_<%=type_name%>_abs_max_kernel_launch(cumo_na_reduction_arg_t* arg)
290
302
  {
291
303
  using impl = cumo_<%=type_name%>_abs_extremum_of<cumo_<%=type_name%>_abs_extremum_rules::Max>;
292
304
  cumo_reduce_split<dtype, rtype, impl>(*arg, impl{});
293
305
  }
306
+ <% end %>
294
307
 
308
+ <% if kernel_part?("abs_min") %>
295
309
  void cumo_<%=type_name%>_abs_min_kernel_launch(cumo_na_reduction_arg_t* arg)
296
310
  {
297
311
  using impl = cumo_<%=type_name%>_abs_extremum_of<cumo_<%=type_name%>_abs_extremum_rules::Min>;
298
312
  cumo_reduce_split<dtype, rtype, impl>(*arg, impl{});
299
313
  }
300
314
  <% end %>
301
- <% if is_double_precision && kernel_part? %>
315
+ <% if is_double_precision && kernel_part?("kahan_sum") %>
302
316
 
303
317
  void cumo_<%=type_name%>_kahan_sum_kernel_launch(cumo_na_reduction_arg_t* arg)
304
318
  {
@@ -162,6 +162,7 @@ struct <%="cumo_thrust_#{name}#{j}"%>
162
162
  template<typename Iterator1, typename Iterator2>
163
163
  static cudaError_t <%="cumo_#{type_name}_#{name}#{j}_scan_by_key"%>(Iterator1 first, Iterator1 last, Iterator2 result, uint64_t row_len)
164
164
  {
165
+ cumo_cuda_cub_prime_ptx_version();
165
166
  cumo_thrust_pool_allocator alloc;
166
167
  <%="cumo_thrust_#{name}_row"%> row = {row_len};
167
168
  auto keys = thrust::make_transform_iterator(thrust::make_counting_iterator<uint64_t>(0), row);
@@ -190,6 +191,7 @@ static cudaError_t <%="cumo_#{type_name}_#{name}#{j}_scan_by_key"%>(Iterator1 fi
190
191
  template<typename Iterator1, typename Iterator2>
191
192
  static cudaError_t <%="cumo_#{type_name}_#{name}#{j}_scan"%>(Iterator1 first, Iterator1 last, Iterator2 result)
192
193
  {
194
+ cumo_cuda_cub_prime_ptx_version();
193
195
  cumo_thrust_pool_allocator alloc;
194
196
  try {
195
197
  <% if widen %>
@@ -181,23 +181,29 @@ extern "C" {
181
181
  #endif
182
182
  #endif
183
183
 
184
- <% if kernel_part? %>
184
+ <% if kernel_part?("mean") %>
185
185
  void cumo_<%=type_name%>_mean_kernel_launch(cumo_na_reduction_arg_t* arg)
186
186
  {
187
187
  <%=acc%> n = (<%=acc%>)(arg->in_indexer.total_size / arg->out_indexer.total_size);
188
188
  cumo_reduce_split<dtype, dtype, cumo_<%=type_name%>_mean_impl>(*arg, cumo_<%=type_name%>_mean_impl{n});
189
189
  }
190
+ <% end %>
190
191
 
192
+ <% if kernel_part?("var") %>
191
193
  void cumo_<%=type_name%>_var_kernel_launch(cumo_na_reduction_arg_t* arg)
192
194
  {
193
195
  cumo_reduce_split<dtype, rtype, cumo_<%=type_name%>_var_impl>(*arg, cumo_<%=type_name%>_var_impl{});
194
196
  }
197
+ <% end %>
195
198
 
199
+ <% if kernel_part?("stddev") %>
196
200
  void cumo_<%=type_name%>_stddev_kernel_launch(cumo_na_reduction_arg_t* arg)
197
201
  {
198
202
  cumo_reduce_split<dtype, rtype, cumo_<%=type_name%>_stddev_impl>(*arg, cumo_<%=type_name%>_stddev_impl{});
199
203
  }
204
+ <% end %>
200
205
 
206
+ <% if kernel_part?("rms") %>
201
207
  void cumo_<%=type_name%>_rms_kernel_launch(cumo_na_reduction_arg_t* arg)
202
208
  {
203
209
  <%=acc%> n = (<%=acc%>)(arg->in_indexer.total_size / arg->out_indexer.total_size);
@@ -226,25 +232,31 @@ void cumo_<%=type_name%>_rms_nan_kernel_launch(cumo_na_reduction_arg_t* arg)
226
232
  cumo_reduce_split<dtype, rtype, cumo_<%=type_name%>_rms_nan_impl>(*arg, cumo_<%=type_name%>_rms_nan_impl{});
227
233
  }
228
234
  <% end %>
229
- <% if %w[sfloat dfloat].include?(type_name) && kernel_part? %>
235
+ <% if %w[sfloat dfloat].include?(type_name) %>
236
+ <% if kernel_part?("abs_sum") %>
230
237
  void cumo_<%=type_name%>_abs_sum_kernel_launch(cumo_na_reduction_arg_t* arg)
231
238
  {
232
239
  cumo_reduce_split<dtype, dtype, cumo_<%=type_name%>_abs_sum_impl>(*arg, cumo_<%=type_name%>_abs_sum_impl{});
233
240
  }
241
+ <% end %>
234
242
 
243
+ <% if kernel_part?("abs_max") %>
235
244
  void cumo_<%=type_name%>_abs_max_kernel_launch(cumo_na_reduction_arg_t* arg)
236
245
  {
237
246
  using impl = cumo_<%=type_name%>_abs_extremum_of<cumo_<%=type_name%>_extremum_rules::Max>;
238
247
  cumo_reduce_split<dtype, dtype, impl>(*arg, impl{});
239
248
  }
249
+ <% end %>
240
250
 
251
+ <% if kernel_part?("abs_min") %>
241
252
  void cumo_<%=type_name%>_abs_min_kernel_launch(cumo_na_reduction_arg_t* arg)
242
253
  {
243
254
  using impl = cumo_<%=type_name%>_abs_extremum_of<cumo_<%=type_name%>_extremum_rules::Min>;
244
255
  cumo_reduce_split<dtype, dtype, impl>(*arg, impl{});
245
256
  }
246
257
  <% end %>
247
- <% if is_double_precision && kernel_part? %>
258
+ <% end %>
259
+ <% if is_double_precision && kernel_part?("kahan_sum") %>
248
260
 
249
261
  void cumo_<%=type_name%>_kahan_sum_kernel_launch(cumo_na_reduction_arg_t* arg)
250
262
  {
@@ -198,54 +198,72 @@ extern "C" {
198
198
  #endif
199
199
  #endif
200
200
 
201
- <% if kernel_part? %>
201
+ <% if kernel_part?("sum") %>
202
202
  void cumo_<%=type_name%>_sum_kernel_launch(cumo_na_reduction_arg_t* arg)
203
203
  {
204
204
  cumo_reduce_split<dtype, <%=dtype%>, cumo_<%=type_name%>_sum_impl>(*arg, cumo_<%=type_name%>_sum_impl{});
205
205
  }
206
+ <% end %>
206
207
 
208
+ <% if kernel_part?("prod") %>
207
209
  void cumo_<%=type_name%>_prod_kernel_launch(cumo_na_reduction_arg_t* arg)
208
210
  {
209
211
  cumo_reduce_split<dtype, <%=dtype%>, cumo_<%=type_name%>_prod_impl>(*arg, cumo_<%=type_name%>_prod_impl{});
210
212
  }
213
+ <% end %>
211
214
 
215
+ <% if kernel_part?("min") %>
212
216
  void cumo_<%=type_name%>_min_kernel_launch(cumo_na_reduction_arg_t* arg)
213
217
  {
214
218
  cumo_reduce_split<dtype, dtype, cumo_<%=type_name%>_min_impl>(*arg, cumo_<%=type_name%>_min_impl{});
215
219
  }
220
+ <% end %>
216
221
 
222
+ <% if kernel_part?("max") %>
217
223
  void cumo_<%=type_name%>_max_kernel_launch(cumo_na_reduction_arg_t* arg)
218
224
  {
219
225
  cumo_reduce_split<dtype, dtype, cumo_<%=type_name%>_max_impl>(*arg, cumo_<%=type_name%>_max_impl{});
220
226
  }
227
+ <% end %>
221
228
 
229
+ <% if kernel_part?("ptp") %>
222
230
  void cumo_<%=type_name%>_ptp_kernel_launch(cumo_na_reduction_arg_t* arg)
223
231
  {
224
232
  cumo_reduce_split<dtype, dtype, cumo_<%=type_name%>_ptp_impl>(*arg, cumo_<%=type_name%>_ptp_impl{});
225
233
  }
234
+ <% end %>
226
235
 
236
+ <% if kernel_part?("minmax") %>
227
237
  void cumo_<%=type_name%>_minmax_kernel_launch(cumo_na_reduction_arg_t* arg, cumo_na_iarray_t* out2)
228
238
  {
229
239
  cumo_reduce_pair_split<dtype, dtype, cumo_<%=type_name%>_minmax_impl>(*arg, *out2, cumo_<%=type_name%>_minmax_impl{});
230
240
  }
241
+ <% end %>
231
242
  <% unless is_float %>
232
243
 
244
+ <% if kernel_part?("mean") %>
233
245
  void cumo_<%=type_name%>_mean_kernel_launch(cumo_na_reduction_arg_t* arg)
234
246
  {
235
247
  double n = (double)(arg->in_indexer.total_size / arg->out_indexer.total_size);
236
248
  cumo_reduce_split<dtype, double, cumo_<%=type_name%>_mean_impl>(*arg, cumo_<%=type_name%>_mean_impl{n});
237
249
  }
250
+ <% end %>
238
251
 
252
+ <% if kernel_part?("var") %>
239
253
  void cumo_<%=type_name%>_var_kernel_launch(cumo_na_reduction_arg_t* arg)
240
254
  {
241
255
  cumo_reduce_split<dtype, double, cumo_<%=type_name%>_var_impl>(*arg, cumo_<%=type_name%>_var_impl{});
242
256
  }
257
+ <% end %>
243
258
 
259
+ <% if kernel_part?("stddev") %>
244
260
  void cumo_<%=type_name%>_stddev_kernel_launch(cumo_na_reduction_arg_t* arg)
245
261
  {
246
262
  cumo_reduce_split<dtype, double, cumo_<%=type_name%>_stddev_impl>(*arg, cumo_<%=type_name%>_stddev_impl{});
247
263
  }
264
+ <% end %>
248
265
 
266
+ <% if kernel_part?("rms") %>
249
267
  void cumo_<%=type_name%>_rms_kernel_launch(cumo_na_reduction_arg_t* arg)
250
268
  {
251
269
  double n = (double)(arg->in_indexer.total_size / arg->out_indexer.total_size);
@@ -0,0 +1,19 @@
1
+ #if defined(__cplusplus)
2
+ #if 0
3
+ { /* satisfy cc-mode */
4
+ #endif
5
+ } /* extern "C" { */
6
+ #endif
7
+
8
+ #include "cumo/sort_kernel.h"
9
+
10
+ // sort_index and median sort with the same kernels, so their entry points are
11
+ // made here too.
12
+ CUMO_DEF_SORT(<%=type_name%>, dtype, <%=is_float%>)
13
+
14
+ #if defined(__cplusplus)
15
+ extern "C" {
16
+ #if 0
17
+ } /* satisfy cc-mode */
18
+ #endif
19
+ #endif
@@ -68,7 +68,7 @@ static void
68
68
  cumo_na_index_set_step(cumo_na_index_arg_t *q, int i, size_t n, size_t beg, ssize_t step)
69
69
  {
70
70
  q->n = n;
71
- q->beg = beg;
71
+ q->beg = n == 0 ? 0 : beg;
72
72
  q->step = step;
73
73
  q->idx = NULL;
74
74
  q->reduce = 0;
@@ -178,6 +178,11 @@ cumo_na_parse_array(VALUE ary, int orig_dim, ssize_t size, cumo_na_index_arg_t *
178
178
  cudaError_t status;
179
179
  int n = RARRAY_LEN(ary);
180
180
 
181
+ if (n == 0) {
182
+ cumo_na_index_set_step(q, orig_dim, 0, 0, 1);
183
+ return;
184
+ }
185
+
181
186
  // A step names the one element a one-element subscript does, without a
182
187
  // device allocation, a kernel to fill it, or the synchronize a loop pays
183
188
  // to read that one entry back. at() is left out: it gathers through an
@@ -250,10 +255,16 @@ cumo_na_parse_narray_index(VALUE a, int orig_dim, ssize_t size, cumo_na_index_ar
250
255
  if (CUMO_NA_SIZE(na) != (size_t)size) {
251
256
  rb_raise(rb_eIndexError, "Bit-NArray size mismatch");
252
257
  }
253
- a = rb_funcall(a, cumo_id_where, 0);
254
- CumoGetNArray(a,na);
258
+ if (size > 0) {
259
+ a = rb_funcall(a, cumo_id_where, 0);
260
+ CumoGetNArray(a,na);
261
+ }
255
262
  }
256
263
  n = CUMO_NA_SIZE(na);
264
+ if (n == 0) {
265
+ cumo_na_index_set_step(q, orig_dim, 0, 0, 1);
266
+ return;
267
+ }
257
268
  idx = cumo_na_new(cIndex,1,&n);
258
269
  cumo_na_store(idx,a);
259
270
 
@@ -301,6 +312,28 @@ cumo_na_parse_narray_index(VALUE a, int orig_dim, ssize_t size, cumo_na_index_ar
301
312
  q->orig_dim = orig_dim;
302
313
  }
303
314
 
315
+ static ssize_t
316
+ cumo_na_range_count(ssize_t beg, ssize_t end, int excl, ssize_t step, ssize_t size)
317
+ {
318
+ ssize_t last;
319
+
320
+ if (step > 0) {
321
+ if (excl ? end <= beg : end < beg) {
322
+ return (beg < 0 || beg > size || end < -1 || end > size) ? -1 : 0;
323
+ }
324
+ last = excl ? end - 1 : end;
325
+ } else {
326
+ if (excl ? end >= beg : end > beg) {
327
+ return (beg < -1 || beg >= size || end < -1 || end > size) ? -1 : 0;
328
+ }
329
+ last = excl ? end + 1 : end;
330
+ }
331
+ if (beg < 0 || beg >= size || last < 0 || last >= size) {
332
+ return -1;
333
+ }
334
+ return (last - beg) / step + 1;
335
+ }
336
+
304
337
  static void
305
338
  cumo_na_parse_range(VALUE range, ssize_t step, int orig_dim, ssize_t size, cumo_na_index_arg_t *q)
306
339
  {
@@ -309,8 +342,8 @@ cumo_na_parse_range(VALUE range, ssize_t step, int orig_dim, ssize_t size, cumo_
309
342
  // caught that: a truncated count came out negative up to 2**32 and was
310
343
  // rounded to an empty view, and past that it came out positive and wrong.
311
344
  ssize_t n;
312
- ssize_t beg, end, beg_orig, end_orig;
313
- const char *dot = "..", *edot = "...";
345
+ ssize_t beg, end;
346
+ int excl;
314
347
 
315
348
  #ifdef HAVE_RB_ARITHMETIC_SEQUENCE_EXTRACT
316
349
  rb_arithmetic_sequence_components_t x;
@@ -323,61 +356,34 @@ cumo_na_parse_range(VALUE range, ssize_t step, int orig_dim, ssize_t size, cumo_
323
356
  rb_raise(rb_eArgError, "step must not be zero");
324
357
  }
325
358
 
326
- beg = beg_orig = NUM2SSIZET(x.begin);
327
- if (beg < 0) {
328
- beg += size;
329
- }
359
+ beg = NUM2SSIZET(x.begin);
330
360
  if (T_NIL == TYPE(x.end)) { // endless range
331
- end = size - 1;
332
- if (RTEST(x.exclude_end)) {
333
- dot = edot;
334
- }
335
- if (beg < 0 || beg >= size) {
336
- rb_raise(rb_eRangeError,
337
- "%"SZF"d%s is out of range for size=%"SZF"d",
338
- beg_orig, dot, size);
339
- }
361
+ end = step > 0 ? size - 1 : 0;
362
+ excl = 0;
340
363
  } else {
341
- end = end_orig = NUM2SSIZET(x.end);
364
+ end = NUM2SSIZET(x.end);
342
365
  if (end < 0) {
343
366
  end += size;
344
367
  }
345
- if (RTEST(x.exclude_end)) {
346
- end--;
347
- dot = edot;
348
- }
349
- if (beg < 0 || beg >= size || end < 0 || end >= size) {
350
- rb_raise(rb_eRangeError,
351
- "%"SZF"d%s%"SZF"d is out of range for size=%"SZF"d",
352
- beg_orig, dot, end_orig, size);
353
- }
368
+ excl = RTEST(x.exclude_end);
354
369
  }
355
370
  #else
356
- VALUE excl_end;
357
-
358
- beg = beg_orig = NUM2SSIZET(rb_funcall(range,cumo_id_beg,0));
359
- if (beg < 0) {
360
- beg += size;
361
- }
362
- end = end_orig = NUM2SSIZET(rb_funcall(range,cumo_id_end,0));
371
+ beg = NUM2SSIZET(rb_funcall(range,cumo_id_beg,0));
372
+ end = NUM2SSIZET(rb_funcall(range,cumo_id_end,0));
363
373
  if (end < 0) {
364
374
  end += size;
365
375
  }
366
- excl_end = rb_funcall(range,cumo_id_exclude_end,0);
367
- if (RTEST(excl_end)) {
368
- end--;
369
- dot = edot;
376
+ excl = RTEST(rb_funcall(range,cumo_id_exclude_end,0));
377
+ #endif
378
+ if (beg < 0) {
379
+ beg += size;
370
380
  }
371
- if (beg < 0 || beg >= size || end < 0 || end >= size) {
372
- rb_raise(rb_eRangeError,
373
- "%"SZF"d%s%"SZF"d is out of range for size=%"SZF"d",
374
- beg_orig, dot, end_orig, size);
381
+ n = cumo_na_range_count(beg, end, excl, step, size);
382
+ if (n < 0) {
383
+ rb_raise(rb_eRangeError, "%"PRIsVALUE" is out of range for size=%"SZF"d",
384
+ rb_inspect(range), size);
375
385
  }
376
- #endif
377
- n = (end-beg)/step+1;
378
- if (n<0) n=0;
379
386
  cumo_na_index_set_step(q,orig_dim,n,beg,step);
380
-
381
387
  }
382
388
 
383
389
  void
@@ -622,6 +628,9 @@ cumo_na_index_aref_nadata(cumo_narray_data_t *na1, cumo_narray_view_t *na2,
622
628
  j++;
623
629
  total *= size;
624
630
  }
631
+ if (total == 0) {
632
+ na2->offset = 0;
633
+ }
625
634
  cumo_na_set_newaxis_strides(na2, newaxis, n_newaxis, j, elmsz);
626
635
  na2->base.size = total;
627
636
  }
@@ -696,10 +705,12 @@ cumo_na_index_aref_naview(cumo_narray_view_t *na1, cumo_narray_view_t *na2,
696
705
  size_t last;
697
706
  stride1 = -stride1;
698
707
  last = na1->base.shape[q[i].orig_dim] - 1;
699
- if (na2->offset < last * stride1) {
700
- rb_raise(rb_eStandardError,"bug: negative offset");
708
+ if (na1->base.size > 0) {
709
+ if (na2->offset < last * stride1) {
710
+ rb_raise(rb_eStandardError,"bug: negative offset");
711
+ }
712
+ na2->offset -= last * stride1;
701
713
  }
702
- na2->offset -= last * stride1;
703
714
  cumo_na_index_aref_naview_index_stride_last_kernel_launch(index, stride1, last, size);
704
715
  } else {
705
716
  cumo_na_index_aref_naview_index_stride_kernel_launch(index, stride1, size);
@@ -727,10 +738,27 @@ cumo_na_index_aref_naview(cumo_narray_view_t *na1, cumo_narray_view_t *na2,
727
738
  j++;
728
739
  total *= size;
729
740
  }
741
+ if (total == 0) {
742
+ na2->offset = na1->offset;
743
+ }
730
744
  cumo_na_set_newaxis_strides(na2, newaxis, n_newaxis, j, elmsz);
731
745
  na2->base.size = total;
732
746
  }
733
747
 
748
+ static size_t
749
+ cumo_na_index_at_size(const cumo_na_index_arg_t *q, int ndim)
750
+ {
751
+ int i;
752
+ size_t size = q[ndim-1].n;
753
+
754
+ for (i=0; i<ndim-1; i++) {
755
+ if (q[i].n != size) {
756
+ rb_raise(cumo_na_eShapeError, "index array sizes mismatch");
757
+ }
758
+ }
759
+ return size;
760
+ }
761
+
734
762
  void cumo_na_index_at_nadata_index_stride_add_kernel_launch(size_t *idx, size_t *idx1, ssize_t s1, uint64_t n);
735
763
  void cumo_na_index_at_nadata_index_beg_step_stride_kernel_launch(size_t *idx, size_t beg, ssize_t step, ssize_t s1, uint64_t n);
736
764
  void cumo_na_index_at_nadata_index_beg_step_stride_add_kernel_launch(size_t *idx, size_t beg, ssize_t step, ssize_t s1, uint64_t n);
@@ -740,7 +768,7 @@ cumo_na_index_at_nadata(cumo_narray_data_t *na1, cumo_narray_view_t *na2,
740
768
  cumo_na_index_arg_t *q, ssize_t elmsz, int ndim, int keep_dim)
741
769
  {
742
770
  int i;
743
- size_t size = q[ndim-1].n;
771
+ size_t size = na2->base.size;
744
772
  ssize_t stride1;
745
773
  ssize_t *strides_na1;
746
774
  size_t *index;
@@ -759,15 +787,6 @@ cumo_na_index_at_nadata(cumo_narray_data_t *na1, cumo_narray_view_t *na2,
759
787
 
760
788
  for (i=ndim-1; i>=0; i--) {
761
789
  stride1 = strides_na1[q[i].orig_dim];
762
- if (i==ndim-1) {
763
- if (size == 0) {
764
- rb_raise(cumo_na_eShapeError, "cannot get element of empty array");
765
- }
766
- } else {
767
- if (size != q[i].n) {
768
- rb_raise(cumo_na_eShapeError, "index array sizes mismatch");
769
- }
770
- }
771
790
 
772
791
  if (q[i].idx != NULL) {
773
792
  if (i==ndim-1) {
@@ -787,8 +806,6 @@ cumo_na_index_at_nadata(cumo_narray_data_t *na1, cumo_narray_view_t *na2,
787
806
  }
788
807
 
789
808
  }
790
- na2->base.size = size;
791
- na2->base.shape[0] = size;
792
809
  }
793
810
 
794
811
  void cumo_na_index_at_naview_index_index_index_add_kernel_launch(size_t *idx, size_t *idx1, size_t *idx2, uint64_t n);
@@ -801,7 +818,7 @@ cumo_na_index_at_naview(cumo_narray_view_t *na1, cumo_narray_view_t *na2,
801
818
  {
802
819
  int i;
803
820
  size_t *index;
804
- size_t size = q[ndim-1].n;
821
+ size_t size = na2->base.size;
805
822
 
806
823
  if (q[ndim-1].idx != NULL) {
807
824
  index = q[ndim-1].idx;
@@ -813,15 +830,6 @@ cumo_na_index_at_naview(cumo_narray_view_t *na1, cumo_narray_view_t *na2,
813
830
 
814
831
  for (i=ndim-1; i>=0; i--) {
815
832
  cumo_stridx_t sdx1 = na1->stridx[q[i].orig_dim];
816
- if (i==ndim-1) {
817
- if (size == 0) {
818
- rb_raise(cumo_na_eShapeError, "cannot get element of empty array");
819
- }
820
- } else {
821
- if (size != q[i].n) {
822
- rb_raise(cumo_na_eShapeError, "index array sizes mismatch");
823
- }
824
- }
825
833
 
826
834
  if (q[i].idx != NULL && CUMO_SDX_IS_INDEX(sdx1)) {
827
835
  // index <- index
@@ -899,8 +907,6 @@ cumo_na_index_at_naview(cumo_narray_view_t *na1, cumo_narray_view_t *na2,
899
907
  }
900
908
  }
901
909
  }
902
- na2->base.size = size;
903
- na2->base.shape[0] = size;
904
910
  }
905
911
 
906
912
  static int
@@ -981,6 +987,13 @@ VALUE cumo_na_aref_md_protected(VALUE data_value)
981
987
 
982
988
  elmsz = cumo_na_element_stride(self);
983
989
 
990
+ if (at_mode && ndim > 0) {
991
+ size_t size = cumo_na_index_at_size(q, ndim);
992
+ na2->base.size = size;
993
+ na2->base.shape[0] = size;
994
+ CUMO_SDX_SET_STRIDE(na2->stridx[0], elmsz);
995
+ }
996
+
984
997
  switch(na1->type) {
985
998
  case CUMO_NARRAY_DATA_T:
986
999
  case CUMO_NARRAY_FILEMAP_T:
@@ -989,7 +1002,9 @@ VALUE cumo_na_aref_md_protected(VALUE data_value)
989
1002
  na2->base.size = 1;
990
1003
  } else {
991
1004
  if (at_mode) {
992
- cumo_na_index_at_nadata((cumo_narray_data_t *)na1,na2,q,elmsz,ndim,keep_dim);
1005
+ if (na2->base.size > 0) {
1006
+ cumo_na_index_at_nadata((cumo_narray_data_t *)na1,na2,q,elmsz,ndim,keep_dim);
1007
+ }
993
1008
  } else {
994
1009
  cumo_na_index_aref_nadata((cumo_narray_data_t *)na1,na2,q,elmsz,ndim,keep_dim);
995
1010
  }
@@ -1005,7 +1020,9 @@ VALUE cumo_na_aref_md_protected(VALUE data_value)
1005
1020
  na2->offset = ((cumo_narray_view_t *)na1)->offset;
1006
1021
  na2->data = ((cumo_narray_view_t *)na1)->data;
1007
1022
  if (at_mode) {
1008
- cumo_na_index_at_naview((cumo_narray_view_t *)na1,na2,q,elmsz,ndim,keep_dim);
1023
+ if (na2->base.size > 0) {
1024
+ cumo_na_index_at_naview((cumo_narray_view_t *)na1,na2,q,elmsz,ndim,keep_dim);
1025
+ }
1009
1026
  } else {
1010
1027
  cumo_na_index_aref_naview((cumo_narray_view_t *)na1,na2,q,elmsz,ndim,keep_dim);
1011
1028
  }
@@ -1201,9 +1218,6 @@ cumo_na_get_result_dimension(VALUE self, int argc, VALUE *argv, ssize_t stride,
1201
1218
  VALUE a;
1202
1219
 
1203
1220
  CumoGetNArray(self,na);
1204
- if (na->size == 0) {
1205
- rb_raise(cumo_na_eShapeError, "cannot get element of empty array");
1206
- }
1207
1221
  idx = ALLOCA_N(ssize_t, argc);
1208
1222
  for (i=j=0; i<argc; i++) {
1209
1223
  a = argv[i];
@@ -1241,13 +1255,15 @@ cumo_na_get_result_dimension(VALUE self, int argc, VALUE *argv, ssize_t stride,
1241
1255
  pos = nv->offset;
1242
1256
  if (j == na->ndim) {
1243
1257
  for (i=j-1; i>=0; i--) {
1244
- x = cumo_na_range_check(idx[i], na->shape[i], i);
1258
+ idx[i] = cumo_na_range_check(idx[i], na->shape[i], i);
1259
+ }
1260
+ for (i=j-1; i>=0; i--) {
1245
1261
  sdx = nv->stridx[i];
1246
1262
  if (CUMO_SDX_IS_INDEX(sdx)) {
1247
1263
  cumo_na_index_wait_fill(nv);
1248
- pos += CUMO_SDX_GET_INDEX(sdx)[x];
1264
+ pos += CUMO_SDX_GET_INDEX(sdx)[idx[i]];
1249
1265
  } else {
1250
- pos += CUMO_SDX_GET_STRIDE(sdx)*x;
1266
+ pos += CUMO_SDX_GET_STRIDE(sdx)*idx[i];
1251
1267
  }
1252
1268
  }
1253
1269
  *pos_idx = pos;
@@ -1820,6 +1820,9 @@ cumo_na_s_from_binary(int argc, VALUE *argv, VALUE type)
1820
1820
  VALUE vstr, vshape, vna;
1821
1821
  VALUE velmsz;
1822
1822
 
1823
+ if (RTEST(rb_class_inherited_p(type, cumo_cRObject))) {
1824
+ rb_raise(rb_eTypeError, "cannot restore %s from binary data", rb_class2name(type));
1825
+ }
1823
1826
  narg = rb_scan_args(argc,argv,"11",&vstr,&vshape);
1824
1827
  Check_Type(vstr,T_STRING);
1825
1828
  str_len = RSTRING_LEN(vstr);
@@ -1931,6 +1934,9 @@ cumo_na_store_binary(int argc, VALUE *argv, VALUE self)
1931
1934
  VALUE velmsz;
1932
1935
  cumo_narray_t *na;
1933
1936
 
1937
+ if (RTEST(rb_obj_is_kind_of(self, cumo_cRObject))) {
1938
+ rb_raise(rb_eTypeError, "cannot store binary data to %s", rb_obj_classname(self));
1939
+ }
1934
1940
  narg = rb_scan_args(argc,argv,"11",&vstr,&voffset);
1935
1941
  Check_Type(vstr,T_STRING);
1936
1942
  if (narg==2) {
@@ -2016,6 +2022,9 @@ cumo_na_to_binary(VALUE self)
2016
2022
  VALUE str;
2017
2023
  cumo_narray_t *na;
2018
2024
 
2025
+ if (RTEST(rb_obj_is_kind_of(self, cumo_cRObject))) {
2026
+ rb_raise(rb_eTypeError, "cannot convert %s into binary data", rb_obj_classname(self));
2027
+ }
2019
2028
  CumoGetNArray(self,na);
2020
2029
  if (na->type == CUMO_NARRAY_VIEW_T &&
2021
2030
  !(cumo_na_view_offset_reaches_bytes(self) && cumo_na_check_contiguous(self)==Qtrue)) {
@@ -53,6 +53,7 @@ typedef struct CUMO_NA_MD_LOOP {
53
53
  // 3 for sum(), 1 for sum(axis: 1), 2 for sum(axis: [1,2])
54
54
  int *trans_map;
55
55
  size_t sub_narray_len; // elements the sub-narray of the current row reaches with, for loop_store_subnarray
56
+ VALUE sub_narray; // keeps the row's sub-narray, which may be a cast copy, visible to the GC until ndloop_release
56
57
  VALUE vargs;
57
58
  VALUE reduce; // dimension indicies to reduce in reduction kernel (in bits), e.g., for an array of shape:
58
59
  // [2,3,4], 111b for sum(), 010b for sum(axis: 1), 110b for sum(axis: [1,2])
@@ -893,6 +894,7 @@ ndloop_set_output(cumo_ndfunc_t *nf, cumo_na_md_loop_t *lp, VALUE args)
893
894
  }
894
895
  LARG(lp,j).value = t;
895
896
  LARG(lp,j).elmsz = sizeof(VALUE);
897
+ rb_ary_push(results, Qnil);
896
898
  } else {
897
899
  rb_raise(rb_eRuntimeError,"ndloop_set_output: invalid for type");
898
900
  }
@@ -2546,6 +2548,7 @@ loop_store_subnarray(cumo_ndfunc_t *nf, cumo_na_md_loop_t *lp, int i0, size_t *c
2546
2548
  if (rb_obj_class(a) != a_type) {
2547
2549
  a = rb_funcall(a_type, cumo_id_cast, 1, a);
2548
2550
  }
2551
+ lp->sub_narray = a;
2549
2552
  CumoGetNArray(a,na);
2550
2553
  if (na->ndim != nd-i0+1) {
2551
2554
  rb_raise(cumo_na_eShapeError, "mismatched dimension of sub-narray: "
@@ -2602,6 +2605,7 @@ loop_store_subnarray(cumo_ndfunc_t *nf, cumo_na_md_loop_t *lp, int i0, size_t *c
2602
2605
  }
2603
2606
  }
2604
2607
  loop_end:
2608
+ LARG(lp,1).value = Qnil;
2605
2609
  LARG(lp,1).ptr = NULL;
2606
2610
  LARG(lp,1).shape = saved_shape;
2607
2611
  LARG(lp,1).elmsz = saved_elmsz;
@@ -271,8 +271,6 @@ cumo_na_step_sequence( VALUE obj, size_t *plen, double *pbeg, double *pstep )
271
271
  void
272
272
  Init_cumo_na_step()
273
273
  {
274
- rb_define_alias(rb_cRange, "%", "step");
275
-
276
274
  cumo_id_beg = rb_intern("begin");
277
275
  cumo_id_end = rb_intern("end");
278
276
  cumo_id_len = rb_intern("length");
data/lib/cumo/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cumo
4
- VERSION = "0.14.1"
4
+ VERSION = "0.15.0"
5
5
 
6
6
  spec = Gem.loaded_specs["cumo"] if defined?(Gem)
7
7
  REVISION = (spec.metadata["cumo_revision"]&.dup&.freeze if spec && spec.full_gem_path == File.expand_path("../..", __dir__))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cumo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.1
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Naotoshi Seo
@@ -38,6 +38,7 @@ files:
38
38
  - Rakefile
39
39
  - cumo.gemspec
40
40
  - docs/src-tree.md
41
+ - ext/cumo/cuda/cub.cu
41
42
  - ext/cumo/cuda/cublas.c
42
43
  - ext/cumo/cuda/cudnn.c
43
44
  - ext/cumo/cuda/cudnn_impl.cpp
@@ -78,6 +79,7 @@ files:
78
79
  - ext/cumo/include/cumo/reduce_kernel.h
79
80
  - ext/cumo/include/cumo/row_kernel.h
80
81
  - ext/cumo/include/cumo/row_method.h
82
+ - ext/cumo/include/cumo/sort_kernel.h
81
83
  - ext/cumo/include/cumo/template.h
82
84
  - ext/cumo/include/cumo/template_kernel.h
83
85
  - ext/cumo/include/cumo/types/bf16_def.h
@@ -271,6 +273,7 @@ files:
271
273
  - ext/cumo/narray/gen/tmpl/softmax_kernel.cu
272
274
  - ext/cumo/narray/gen/tmpl/sort.c
273
275
  - ext/cumo/narray/gen/tmpl/sort_index.c
276
+ - ext/cumo/narray/gen/tmpl/sort_kernel.cu
274
277
  - ext/cumo/narray/gen/tmpl/store.c
275
278
  - ext/cumo/narray/gen/tmpl/store_array.c
276
279
  - ext/cumo/narray/gen/tmpl/store_array_kernel.cu
@@ -334,7 +337,6 @@ files:
334
337
  - ext/cumo/narray/ndloop.c
335
338
  - ext/cumo/narray/ndloop_kernel.cu
336
339
  - ext/cumo/narray/rand.c
337
- - ext/cumo/narray/sort_kernel.cu
338
340
  - ext/cumo/narray/step.c
339
341
  - ext/cumo/narray/struct.c
340
342
  - lib/cumo.rb
@@ -370,7 +372,7 @@ metadata:
370
372
  changelog_uri: https://github.com/sonots/cumo/blob/master/CHANGELOG.md
371
373
  bug_tracker_uri: https://github.com/sonots/cumo/issues
372
374
  rubygems_mfa_required: 'true'
373
- cumo_revision: v0.14.1
375
+ cumo_revision: v0.15.0
374
376
  rdoc_options: []
375
377
  require_paths:
376
378
  - lib