numo-narray-alt 0.9.3

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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +14 -0
  3. data/LICENSE +30 -0
  4. data/README.md +71 -0
  5. data/Rakefile +24 -0
  6. data/ext/numo/narray/SFMT-params.h +97 -0
  7. data/ext/numo/narray/SFMT-params19937.h +48 -0
  8. data/ext/numo/narray/SFMT.c +602 -0
  9. data/ext/numo/narray/SFMT.h +147 -0
  10. data/ext/numo/narray/array.c +575 -0
  11. data/ext/numo/narray/data.c +958 -0
  12. data/ext/numo/narray/extconf.rb +84 -0
  13. data/ext/numo/narray/index.c +1092 -0
  14. data/ext/numo/narray/kwargs.c +142 -0
  15. data/ext/numo/narray/math.c +133 -0
  16. data/ext/numo/narray/narray.c +1976 -0
  17. data/ext/numo/narray/narray.def +28 -0
  18. data/ext/numo/narray/ndloop.c +1840 -0
  19. data/ext/numo/narray/numo/compat.h +23 -0
  20. data/ext/numo/narray/numo/intern.h +115 -0
  21. data/ext/numo/narray/numo/narray.h +480 -0
  22. data/ext/numo/narray/numo/ndloop.h +93 -0
  23. data/ext/numo/narray/numo/template.h +149 -0
  24. data/ext/numo/narray/numo/types/bit.h +38 -0
  25. data/ext/numo/narray/numo/types/complex.h +404 -0
  26. data/ext/numo/narray/numo/types/complex_macro.h +384 -0
  27. data/ext/numo/narray/numo/types/dcomplex.h +42 -0
  28. data/ext/numo/narray/numo/types/dfloat.h +44 -0
  29. data/ext/numo/narray/numo/types/float_def.h +34 -0
  30. data/ext/numo/narray/numo/types/float_macro.h +202 -0
  31. data/ext/numo/narray/numo/types/int16.h +27 -0
  32. data/ext/numo/narray/numo/types/int32.h +23 -0
  33. data/ext/numo/narray/numo/types/int64.h +23 -0
  34. data/ext/numo/narray/numo/types/int8.h +23 -0
  35. data/ext/numo/narray/numo/types/int_macro.h +66 -0
  36. data/ext/numo/narray/numo/types/real_accum.h +481 -0
  37. data/ext/numo/narray/numo/types/robj_macro.h +78 -0
  38. data/ext/numo/narray/numo/types/robject.h +25 -0
  39. data/ext/numo/narray/numo/types/scomplex.h +42 -0
  40. data/ext/numo/narray/numo/types/sfloat.h +45 -0
  41. data/ext/numo/narray/numo/types/uint16.h +24 -0
  42. data/ext/numo/narray/numo/types/uint32.h +20 -0
  43. data/ext/numo/narray/numo/types/uint64.h +20 -0
  44. data/ext/numo/narray/numo/types/uint8.h +20 -0
  45. data/ext/numo/narray/numo/types/uint_macro.h +57 -0
  46. data/ext/numo/narray/numo/types/xint_macro.h +166 -0
  47. data/ext/numo/narray/rand.c +40 -0
  48. data/ext/numo/narray/src/t_bit.c +3236 -0
  49. data/ext/numo/narray/src/t_dcomplex.c +6776 -0
  50. data/ext/numo/narray/src/t_dfloat.c +9417 -0
  51. data/ext/numo/narray/src/t_int16.c +5757 -0
  52. data/ext/numo/narray/src/t_int32.c +5757 -0
  53. data/ext/numo/narray/src/t_int64.c +5759 -0
  54. data/ext/numo/narray/src/t_int8.c +5355 -0
  55. data/ext/numo/narray/src/t_robject.c +5567 -0
  56. data/ext/numo/narray/src/t_scomplex.c +6731 -0
  57. data/ext/numo/narray/src/t_sfloat.c +9374 -0
  58. data/ext/numo/narray/src/t_uint16.c +5753 -0
  59. data/ext/numo/narray/src/t_uint32.c +5753 -0
  60. data/ext/numo/narray/src/t_uint64.c +5755 -0
  61. data/ext/numo/narray/src/t_uint8.c +5351 -0
  62. data/ext/numo/narray/step.c +266 -0
  63. data/ext/numo/narray/struct.c +814 -0
  64. data/lib/numo/narray/extra.rb +1266 -0
  65. data/lib/numo/narray.rb +4 -0
  66. metadata +106 -0
@@ -0,0 +1,1266 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Numo
4
+ class NArray
5
+ # Return an unallocated array with the same shape and type as self.
6
+ def new_narray
7
+ self.class.new(*shape)
8
+ end
9
+
10
+ # Return an array of zeros with the same shape and type as self.
11
+ def new_zeros
12
+ self.class.zeros(*shape)
13
+ end
14
+
15
+ # Return an array of ones with the same shape and type as self.
16
+ def new_ones
17
+ self.class.ones(*shape)
18
+ end
19
+
20
+ # Return an array filled with value with the same shape and type as self.
21
+ def new_fill(value)
22
+ self.class.new(*shape).fill(value)
23
+ end
24
+
25
+ # Convert angles from radians to degrees.
26
+ def rad2deg
27
+ self * (180 / Math::PI)
28
+ end
29
+
30
+ # Convert angles from degrees to radians.
31
+ def deg2rad
32
+ self * (Math::PI / 180)
33
+ end
34
+
35
+ # Flip each row in the left/right direction.
36
+ # Same as `a[true, (-1..0).step(-1), ...]`.
37
+ def fliplr
38
+ reverse(1)
39
+ end
40
+
41
+ # Flip each column in the up/down direction.
42
+ # Same as `a[(-1..0).step(-1), ...]`.
43
+ def flipud
44
+ reverse(0)
45
+ end
46
+
47
+ # Rotate in the plane specified by axes.
48
+ # @example
49
+ # a = Numo::Int32.new(2,2).seq
50
+ # # => Numo::Int32#shape=[2,2]
51
+ # # [[0, 1],
52
+ # # [2, 3]]
53
+ #
54
+ # a.rot90
55
+ # # => Numo::Int32(view)#shape=[2,2]
56
+ # # [[1, 3],
57
+ # # [0, 2]]
58
+ #
59
+ # a.rot90(2)
60
+ # # => Numo::Int32(view)#shape=[2,2]
61
+ # # [[3, 2],
62
+ # # [1, 0]]
63
+ #
64
+ # a.rot90(3)
65
+ # # => Numo::Int32(view)#shape=[2,2]
66
+ # # [[2, 0],
67
+ # # [3, 1]]
68
+ def rot90(k = 1, axes = [0, 1])
69
+ case k % 4
70
+ when 0
71
+ view
72
+ when 1
73
+ swapaxes(*axes).reverse(axes[0])
74
+ when 2
75
+ reverse(*axes)
76
+ when 3
77
+ swapaxes(*axes).reverse(axes[1])
78
+ end
79
+ end
80
+
81
+ def to_i
82
+ # convert to Int?
83
+ raise TypeError, "can't convert #{self.class} into Integer" unless size == 1
84
+
85
+ self[0].to_i
86
+ end
87
+
88
+ def to_f
89
+ # convert to DFloat?
90
+ raise TypeError, "can't convert #{self.class} into Float" unless size == 1
91
+
92
+ self[0].to_f
93
+ end
94
+
95
+ def to_c
96
+ # convert to DComplex?
97
+ raise TypeError, "can't convert #{self.class} into Complex" unless size == 1
98
+
99
+ Complex(self[0])
100
+ end
101
+
102
+ # Convert the argument to an narray if not an narray.
103
+ def self.cast(a)
104
+ case a
105
+ when NArray
106
+ a
107
+ when Array, Numeric
108
+ NArray.array_type(a).cast(a)
109
+ else
110
+ raise TypeError, 'invalid type for NArray' unless a.respond_to?(:to_a)
111
+
112
+ a = a.to_a
113
+ NArray.array_type(a).cast(a)
114
+ end
115
+ end
116
+
117
+ def self.asarray(a)
118
+ case a
119
+ when NArray
120
+ a.ndim == 0 ? a[:new] : a
121
+ when Numeric, Range
122
+ self[a]
123
+ else
124
+ cast(a)
125
+ end
126
+ end
127
+
128
+ # parse matrix like matlab, octave
129
+ # @example
130
+ # a = Numo::DFloat.parse %[
131
+ # 2 -3 5
132
+ # 4 9 7
133
+ # 2 -1 6
134
+ # ]
135
+ # # => Numo::DFloat#shape=[3,3]
136
+ # # [[2, -3, 5],
137
+ # # [4, 9, 7],
138
+ # # [2, -1, 6]]
139
+
140
+ def self.parse(str, split1d: /\s+/, split2d: /;?$|;/,
141
+ split3d: /\s*\n(\s*\n)+/m)
142
+ a = []
143
+ str.split(split3d).each do |block|
144
+ b = []
145
+ # print "b"; p block
146
+ block.split(split2d).each do |line|
147
+ # p line
148
+ line.strip!
149
+ next if line.empty?
150
+
151
+ c = []
152
+ line.split(split1d).each do |item|
153
+ c << eval(item.strip) unless item.empty? # rubocop:disable Security/Eval
154
+ end
155
+ b << c unless c.empty?
156
+ end
157
+ a << b unless b.empty?
158
+ end
159
+ if a.size == 1
160
+ cast(a[0])
161
+ else
162
+ cast(a)
163
+ end
164
+ end
165
+
166
+ # Iterate over an axis
167
+ # @example
168
+ # > a = Numo::DFloat.new(2,2,2).seq
169
+ # > p a
170
+ # Numo::DFloat#shape=[2,2,2]
171
+ # [[[0, 1],
172
+ # [2, 3]],
173
+ # [[4, 5],
174
+ # [6, 7]]]
175
+ #
176
+ # > a.each_over_axis{|i| p i}
177
+ # Numo::DFloat(view)#shape=[2,2]
178
+ # [[0, 1],
179
+ # [2, 3]]
180
+ # Numo::DFloat(view)#shape=[2,2]
181
+ # [[4, 5],
182
+ # [6, 7]]
183
+ #
184
+ # > a.each_over_axis(1){|i| p i}
185
+ # Numo::DFloat(view)#shape=[2,2]
186
+ # [[0, 1],
187
+ # [4, 5]]
188
+ # Numo::DFloat(view)#shape=[2,2]
189
+ # [[2, 3],
190
+ # [6, 7]]
191
+
192
+ def each_over_axis(axis = 0)
193
+ return to_enum(:each_over_axis, axis) unless block_given?
194
+
195
+ if ndim == 0
196
+ raise ArgumentError, "axis=#{axis} is invalid" if axis != 0
197
+
198
+ niter = 1
199
+ else
200
+ axis = check_axis(axis)
201
+ niter = shape[axis]
202
+ end
203
+ idx = [true] * ndim
204
+ niter.times do |i|
205
+ idx[axis] = i
206
+ yield(self[*idx])
207
+ end
208
+ self
209
+ end
210
+
211
+ # Append values to the end of an narray.
212
+ # @example
213
+ # a = Numo::DFloat[1, 2, 3]
214
+ # a.append([[4, 5, 6], [7, 8, 9]])
215
+ # # => Numo::DFloat#shape=[9]
216
+ # # [1, 2, 3, 4, 5, 6, 7, 8, 9]
217
+ #
218
+ # a = Numo::DFloat[[1, 2, 3]]
219
+ # a.append([[4, 5, 6], [7, 8, 9]],axis:0)
220
+ # # => Numo::DFloat#shape=[3,3]
221
+ # # [[1, 2, 3],
222
+ # # [4, 5, 6],
223
+ # # [7, 8, 9]]
224
+ #
225
+ # a = Numo::DFloat[[1, 2, 3], [4, 5, 6]]
226
+ # a.append([7, 8, 9], axis:0)
227
+ # # in `append': dimension mismatch (Numo::NArray::DimensionError)
228
+
229
+ def append(other, axis: nil)
230
+ other = self.class.cast(other)
231
+ if axis
232
+ raise DimensionError, 'dimension mismatch' if ndim != other.ndim
233
+
234
+ concatenate(other, axis: axis)
235
+ else
236
+ a = self.class.zeros(size + other.size)
237
+ a[0...size] = self[true]
238
+ a[size..-1] = other[true]
239
+ a
240
+ end
241
+ end
242
+
243
+ # Return a new array with sub-arrays along an axis deleted.
244
+ # If axis is not given, obj is applied to the flattened array.
245
+
246
+ # @example
247
+ # a = Numo::DFloat[[1,2,3,4], [5,6,7,8], [9,10,11,12]]
248
+ # a.delete(1,0)
249
+ # # => Numo::DFloat(view)#shape=[2,4]
250
+ # # [[1, 2, 3, 4],
251
+ # # [9, 10, 11, 12]]
252
+ #
253
+ # a.delete((0..-1).step(2),1)
254
+ # # => Numo::DFloat(view)#shape=[3,2]
255
+ # # [[2, 4],
256
+ # # [6, 8],
257
+ # # [10, 12]]
258
+ #
259
+ # a.delete([1,3,5])
260
+ # # => Numo::DFloat(view)#shape=[9]
261
+ # # [1, 3, 5, 7, 8, 9, 10, 11, 12]
262
+
263
+ def delete(indice, axis = nil)
264
+ if axis
265
+ bit = Bit.ones(shape[axis])
266
+ bit[indice] = 0
267
+ idx = [true] * ndim
268
+ idx[axis] = bit.where
269
+ self[*idx].copy
270
+ else
271
+ bit = Bit.ones(size)
272
+ bit[indice] = 0
273
+ self[bit.where].copy
274
+ end
275
+ end
276
+
277
+ # Insert values along the axis before the indices.
278
+ # @example
279
+ # a = Numo::DFloat[[1, 2], [3, 4]]
280
+ # a = Numo::Int32[[1, 1], [2, 2], [3, 3]]
281
+ #
282
+ # a.insert(1,5)
283
+ # # => Numo::Int32#shape=[7]
284
+ # # [1, 5, 1, 2, 2, 3, 3]
285
+ #
286
+ # a.insert(1, 5, axis:1)
287
+ # # => Numo::Int32#shape=[3,3]
288
+ # # [[1, 5, 1],
289
+ # # [2, 5, 2],
290
+ # # [3, 5, 3]]
291
+ #
292
+ # a.insert([1], [[11],[12],[13]], axis:1)
293
+ # # => Numo::Int32#shape=[3,3]
294
+ # # [[1, 11, 1],
295
+ # # [2, 12, 2],
296
+ # # [3, 13, 3]]
297
+ #
298
+ # a.insert(1, [11, 12, 13], axis:1)
299
+ # # => Numo::Int32#shape=[3,3]
300
+ # # [[1, 11, 1],
301
+ # # [2, 12, 2],
302
+ # # [3, 13, 3]]
303
+ #
304
+ # a.insert([1], [11, 12, 13], axis:1)
305
+ # # => Numo::Int32#shape=[3,5]
306
+ # # [[1, 11, 12, 13, 1],
307
+ # # [2, 11, 12, 13, 2],
308
+ # # [3, 11, 12, 13, 3]]
309
+ #
310
+ # b = a.flatten
311
+ # # => Numo::Int32(view)#shape=[6]
312
+ # # [1, 1, 2, 2, 3, 3]
313
+ #
314
+ # b.insert(2,[15,16])
315
+ # # => Numo::Int32#shape=[8]
316
+ # # [1, 1, 15, 16, 2, 2, 3, 3]
317
+ #
318
+ # b.insert([2,2],[15,16])
319
+ # # => Numo::Int32#shape=[8]
320
+ # # [1, 1, 15, 16, 2, 2, 3, 3]
321
+ #
322
+ # b.insert([2,1],[15,16])
323
+ # # => Numo::Int32#shape=[8]
324
+ # # [1, 16, 1, 15, 2, 2, 3, 3]
325
+ #
326
+ # b.insert([2,0,1],[15,16,17])
327
+ # # => Numo::Int32#shape=[9]
328
+ # # [16, 1, 17, 1, 15, 2, 2, 3, 3]
329
+ #
330
+ # b.insert(2..3, [15, 16])
331
+ # # => Numo::Int32#shape=[8]
332
+ # # [1, 1, 15, 2, 16, 2, 3, 3]
333
+ #
334
+ # b.insert(2, [7.13, 0.5])
335
+ # # => Numo::Int32#shape=[8]
336
+ # # [1, 1, 7, 0, 2, 2, 3, 3]
337
+ #
338
+ # x = Numo::DFloat.new(2,4).seq
339
+ # # => Numo::DFloat#shape=[2,4]
340
+ # # [[0, 1, 2, 3],
341
+ # # [4, 5, 6, 7]]
342
+ #
343
+ # x.insert([1,3],999,axis:1)
344
+ # # => Numo::DFloat#shape=[2,6]
345
+ # # [[0, 999, 1, 2, 999, 3],
346
+ # # [4, 999, 5, 6, 999, 7]]
347
+
348
+ def insert(indice, values, axis: nil)
349
+ if axis
350
+ values = self.class.asarray(values)
351
+ nd = values.ndim
352
+ midx = ([:new] * (ndim - nd)) + ([true] * nd)
353
+ case indice
354
+ when Numeric
355
+ midx[-nd - 1] = true
356
+ midx[axis] = :new
357
+ end
358
+ values = values[*midx]
359
+ else
360
+ values = self.class.asarray(values).flatten
361
+ end
362
+ idx = Int64.asarray(indice)
363
+ nidx = idx.size
364
+ if nidx == 1
365
+ nidx = values.shape[axis || 0]
366
+ idx += Int64.new(nidx).seq
367
+ else
368
+ sidx = idx.sort_index
369
+ idx[sidx] += Int64.new(nidx).seq
370
+ end
371
+ if axis
372
+ bit = Bit.ones(shape[axis] + nidx)
373
+ bit[idx] = 0
374
+ new_shape = shape
375
+ new_shape[axis] += nidx
376
+ a = self.class.zeros(new_shape)
377
+ mdidx = [true] * ndim
378
+ mdidx[axis] = bit.where
379
+ a[*mdidx] = self
380
+ mdidx[axis] = idx
381
+ a[*mdidx] = values
382
+ else
383
+ bit = Bit.ones(size + nidx)
384
+ bit[idx] = 0
385
+ a = self.class.zeros(size + nidx)
386
+ a[bit.where] = flatten
387
+ a[idx] = values
388
+ end
389
+ a
390
+ end
391
+
392
+ class << self
393
+ # @example
394
+ # a = Numo::DFloat[[1, 2], [3, 4]]
395
+ # # => Numo::DFloat#shape=[2,2]
396
+ # # [[1, 2],
397
+ # # [3, 4]]
398
+ #
399
+ # b = Numo::DFloat[[5, 6]]
400
+ # # => Numo::DFloat#shape=[1,2]
401
+ # # [[5, 6]]
402
+ #
403
+ # Numo::NArray.concatenate([a,b],axis:0)
404
+ # # => Numo::DFloat#shape=[3,2]
405
+ # # [[1, 2],
406
+ # # [3, 4],
407
+ # # [5, 6]]
408
+ #
409
+ # Numo::NArray.concatenate([a,b.transpose], axis:1)
410
+ # # => Numo::DFloat#shape=[2,3]
411
+ # # [[1, 2, 5],
412
+ # # [3, 4, 6]]
413
+
414
+ def concatenate(arrays, axis: 0)
415
+ klass = self == NArray ? NArray.array_type(arrays) : self
416
+ nd = 0
417
+ arrays = arrays.map do |a|
418
+ case a
419
+ when NArray
420
+ # ok
421
+ when Numeric
422
+ a = klass[a]
423
+ when Array
424
+ a = klass.cast(a)
425
+ else
426
+ raise TypeError, "not Numo::NArray: #{a.inspect[0..48]}"
427
+ end
428
+ nd = a.ndim if a.ndim > nd
429
+ a
430
+ end
431
+ axis += nd if axis < 0
432
+ raise ArgumentError, 'axis is out of range' if axis < 0 || axis >= nd
433
+
434
+ new_shape = nil
435
+ sum_size = 0
436
+ arrays.each do |a|
437
+ a_shape = a.shape
438
+ a_shape = ([1] * (nd - a_shape.size)) + a_shape if nd != a_shape.size # rubocop:disable Performance/CollectionLiteralInLoop
439
+ sum_size += a_shape.delete_at(axis)
440
+ if new_shape
441
+ raise ShapeError, 'shape mismatch' if new_shape != a_shape
442
+ else
443
+ new_shape = a_shape
444
+ end
445
+ end
446
+ new_shape.insert(axis, sum_size)
447
+ result = klass.zeros(*new_shape)
448
+ lst = 0
449
+ refs = [true] * nd
450
+ arrays.each do |a|
451
+ fst = lst
452
+ lst = fst + (a.shape[axis - nd] || 1)
453
+ if lst > fst
454
+ refs[axis] = fst...lst
455
+ result[*refs] = a
456
+ end
457
+ end
458
+ result
459
+ end
460
+
461
+ # Stack arrays vertically (row wise).
462
+ # @example
463
+ # a = Numo::Int32[1,2,3]
464
+ # b = Numo::Int32[2,3,4]
465
+ # Numo::NArray.vstack([a,b])
466
+ # # => Numo::Int32#shape=[2,3]
467
+ # # [[1, 2, 3],
468
+ # # [2, 3, 4]]
469
+ #
470
+ # a = Numo::Int32[[1],[2],[3]]
471
+ # b = Numo::Int32[[2],[3],[4]]
472
+ # Numo::NArray.vstack([a,b])
473
+ # # => Numo::Int32#shape=[6,1]
474
+ # # [[1],
475
+ # # [2],
476
+ # # [3],
477
+ # # [2],
478
+ # # [3],
479
+ # # [4]]
480
+
481
+ def vstack(arrays)
482
+ arys = arrays.map do |a|
483
+ _atleast_2d(cast(a))
484
+ end
485
+ concatenate(arys, axis: 0)
486
+ end
487
+
488
+ # Stack arrays horizontally (column wise).
489
+ # @example
490
+ # a = Numo::Int32[1,2,3]
491
+ # b = Numo::Int32[2,3,4]
492
+ # Numo::NArray.hstack([a,b])
493
+ # # => Numo::Int32#shape=[6]
494
+ # # [1, 2, 3, 2, 3, 4]
495
+ #
496
+ # a = Numo::Int32[[1],[2],[3]]
497
+ # b = Numo::Int32[[2],[3],[4]]
498
+ # Numo::NArray.hstack([a,b])
499
+ # # => Numo::Int32#shape=[3,2]
500
+ # # [[1, 2],
501
+ # # [2, 3],
502
+ # # [3, 4]]
503
+
504
+ def hstack(arrays)
505
+ klass = self == NArray ? NArray.array_type(arrays) : self
506
+ nd = 0
507
+ arys = arrays.map do |a|
508
+ a = klass.cast(a)
509
+ nd = a.ndim if a.ndim > nd
510
+ a
511
+ end
512
+ dim = nd >= 2 ? 1 : 0
513
+ concatenate(arys, axis: dim)
514
+ end
515
+
516
+ # Stack arrays in depth wise (along third axis).
517
+ # @example
518
+ # a = Numo::Int32[1,2,3]
519
+ # b = Numo::Int32[2,3,4]
520
+ # Numo::NArray.dstack([a,b])
521
+ # # => Numo::Int32#shape=[1,3,2]
522
+ # # [[[1, 2],
523
+ # # [2, 3],
524
+ # # [3, 4]]]
525
+ #
526
+ # a = Numo::Int32[[1],[2],[3]]
527
+ # b = Numo::Int32[[2],[3],[4]]
528
+ # Numo::NArray.dstack([a,b])
529
+ # # => Numo::Int32#shape=[3,1,2]
530
+ # # [[[1, 2]],
531
+ # # [[2, 3]],
532
+ # # [[3, 4]]]
533
+
534
+ def dstack(arrays)
535
+ arys = arrays.map do |a|
536
+ _atleast_3d(cast(a))
537
+ end
538
+ concatenate(arys, axis: 2)
539
+ end
540
+
541
+ # Stack 1-d arrays into columns of a 2-d array.
542
+ # @example
543
+ # x = Numo::Int32[1,2,3]
544
+ # y = Numo::Int32[2,3,4]
545
+ # Numo::NArray.column_stack([x,y])
546
+ # # => Numo::Int32#shape=[3,2]
547
+ # # [[1, 2],
548
+ # # [2, 3],
549
+ # # [3, 4]]
550
+
551
+ def column_stack(arrays)
552
+ arys = arrays.map do |a|
553
+ a = cast(a)
554
+ case a.ndim
555
+ when 0 then a[:new, :new]
556
+ when 1 then a[true, :new]
557
+ else; a
558
+ end
559
+ end
560
+ concatenate(arys, axis: 1)
561
+ end
562
+
563
+ private
564
+
565
+ # Return an narray with at least two dimension.
566
+ def _atleast_2d(a)
567
+ case a.ndim
568
+ when 0 then a[:new, :new]
569
+ when 1 then a[:new, true]
570
+ else; a
571
+ end
572
+ end
573
+
574
+ # Return an narray with at least three dimension.
575
+ def _atleast_3d(a)
576
+ case a.ndim
577
+ when 0 then a[:new, :new, :new]
578
+ when 1 then a[:new, true, :new]
579
+ when 2 then a[true, true, :new]
580
+ else; a
581
+ end
582
+ end
583
+ end
584
+
585
+ # @example
586
+ # a = Numo::DFloat[[1, 2], [3, 4]]
587
+ # # => Numo::DFloat#shape=[2,2]
588
+ # # [[1, 2],
589
+ # # [3, 4]]
590
+ #
591
+ # b = Numo::DFloat[[5, 6]]
592
+ # # => Numo::DFloat#shape=[1,2]
593
+ # # [[5, 6]]
594
+ #
595
+ # a.concatenate(b,axis:0)
596
+ # # => Numo::DFloat#shape=[3,2]
597
+ # # [[1, 2],
598
+ # # [3, 4],
599
+ # # [5, 6]]
600
+ #
601
+ # a.concatenate(b.transpose, axis:1)
602
+ # # => Numo::DFloat#shape=[2,3]
603
+ # # [[1, 2, 5],
604
+ # # [3, 4, 6]]
605
+
606
+ def concatenate(*arrays, axis: 0)
607
+ axis = check_axis(axis)
608
+ self_shape = shape
609
+ self_shape.delete_at(axis)
610
+ sum_size = shape[axis]
611
+ arrays.map! do |a|
612
+ case a
613
+ when NArray
614
+ # ok
615
+ when Numeric
616
+ a = self.class.new(1).store(a)
617
+ when Array
618
+ a = self.class.cast(a)
619
+ else
620
+ raise TypeError, "not Numo::NArray: #{a.inspect[0..48]}"
621
+ end
622
+ raise ShapeError, 'dimension mismatch' if a.ndim > ndim
623
+
624
+ a_shape = a.shape
625
+ sum_size += a_shape.delete_at(axis - ndim) || 1
626
+ raise ShapeError, 'shape mismatch' if self_shape != a_shape
627
+
628
+ a
629
+ end
630
+ self_shape.insert(axis, sum_size)
631
+ result = self.class.zeros(*self_shape)
632
+ lst = shape[axis]
633
+ refs = [true] * ndim
634
+ if lst > 0
635
+ refs[axis] = 0...lst
636
+ result[*refs] = self
637
+ end
638
+ arrays.each do |a|
639
+ fst = lst
640
+ lst = fst + (a.shape[axis - ndim] || 1)
641
+ if lst > fst
642
+ refs[axis] = fst...lst
643
+ result[*refs] = a
644
+ end
645
+ end
646
+ result
647
+ end
648
+
649
+ # @example
650
+ # x = Numo::DFloat.new(9).seq
651
+ # # => Numo::DFloat#shape=[9]
652
+ # # [0, 1, 2, 3, 4, 5, 6, 7, 8]
653
+ #
654
+ # x.split(3)
655
+ # # => [Numo::DFloat(view)#shape=[3]
656
+ # # [0, 1, 2],
657
+ # # Numo::DFloat(view)#shape=[3]
658
+ # # [3, 4, 5],
659
+ # # Numo::DFloat(view)#shape=[3]
660
+ # # [6, 7, 8]]
661
+ #
662
+ # x = Numo::DFloat.new(8).seq
663
+ # # => Numo::DFloat#shape=[8]
664
+ # # [0, 1, 2, 3, 4, 5, 6, 7]
665
+ #
666
+ # x.split([3, 5, 6, 10])
667
+ # # => [Numo::DFloat(view)#shape=[3]
668
+ # # [0, 1, 2],
669
+ # # Numo::DFloat(view)#shape=[2]
670
+ # # [3, 4],
671
+ # # Numo::DFloat(view)#shape=[1]
672
+ # # [5],
673
+ # # Numo::DFloat(view)#shape=[2]
674
+ # # [6, 7],
675
+ # # Numo::DFloat(view)#shape=[0][]]
676
+
677
+ def split(indices_or_sections, axis: 0)
678
+ axis = check_axis(axis)
679
+ size_axis = shape[axis]
680
+ case indices_or_sections
681
+ when Integer
682
+ div_axis, mod_axis = size_axis.divmod(indices_or_sections)
683
+ refs = [true] * ndim
684
+ beg_idx = 0
685
+ Array.new(mod_axis) do |_i|
686
+ end_idx = beg_idx + div_axis + 1
687
+ refs[axis] = beg_idx...end_idx
688
+ beg_idx = end_idx
689
+ self[*refs]
690
+ end +
691
+ Array.new(indices_or_sections - mod_axis) do |_i|
692
+ end_idx = beg_idx + div_axis
693
+ refs[axis] = beg_idx...end_idx
694
+ beg_idx = end_idx
695
+ self[*refs]
696
+ end
697
+ when NArray
698
+ split(indices_or_sections.to_a, axis: axis)
699
+ when Array
700
+ refs = [true] * ndim
701
+ fst = 0
702
+ (indices_or_sections + [size_axis]).map do |lst|
703
+ lst = size_axis if lst > size_axis
704
+ refs[axis] = fst < size_axis ? fst...lst : -1...-1
705
+ fst = lst
706
+ self[*refs]
707
+ end
708
+ else
709
+ raise TypeError, 'argument must be Integer or Array'
710
+ end
711
+ end
712
+
713
+ # @example
714
+ # x = Numo::DFloat.new(4,4).seq
715
+ # # => Numo::DFloat#shape=[4,4]
716
+ # # [[0, 1, 2, 3],
717
+ # # [4, 5, 6, 7],
718
+ # # [8, 9, 10, 11],
719
+ # # [12, 13, 14, 15]]
720
+ #
721
+ # x.hsplit(2)
722
+ # # => [Numo::DFloat(view)#shape=[4,2]
723
+ # # [[0, 1],
724
+ # # [4, 5],
725
+ # # [8, 9],
726
+ # # [12, 13]],
727
+ # # Numo::DFloat(view)#shape=[4,2]
728
+ # # [[2, 3],
729
+ # # [6, 7],
730
+ # # [10, 11],
731
+ # # [14, 15]]]
732
+ #
733
+ # x.hsplit([3, 6])
734
+ # # => [Numo::DFloat(view)#shape=[4,3]
735
+ # # [[0, 1, 2],
736
+ # # [4, 5, 6],
737
+ # # [8, 9, 10],
738
+ # # [12, 13, 14]],
739
+ # # Numo::DFloat(view)#shape=[4,1]
740
+ # # [[3],
741
+ # # [7],
742
+ # # [11],
743
+ # # [15]],
744
+ # # Numo::DFloat(view)#shape=[4,0][]]
745
+
746
+ def vsplit(indices_or_sections)
747
+ split(indices_or_sections, axis: 0)
748
+ end
749
+
750
+ def hsplit(indices_or_sections)
751
+ split(indices_or_sections, axis: 1)
752
+ end
753
+
754
+ def dsplit(indices_or_sections)
755
+ split(indices_or_sections, axis: 2)
756
+ end
757
+
758
+ # @example
759
+ # a = Numo::NArray[0,1,2]
760
+ # # => Numo::Int32#shape=[3]
761
+ # # [0, 1, 2]
762
+ #
763
+ # a.tile(2)
764
+ # # => Numo::Int32#shape=[6]
765
+ # # [0, 1, 2, 0, 1, 2]
766
+ #
767
+ # a.tile(2,2)
768
+ # # => Numo::Int32#shape=[2,6]
769
+ # # [[0, 1, 2, 0, 1, 2],
770
+ # # [0, 1, 2, 0, 1, 2]]
771
+ #
772
+ # a.tile(2,1,2)
773
+ # # => Numo::Int32#shape=[2,1,6]
774
+ # # [[[0, 1, 2, 0, 1, 2]],
775
+ # # [[0, 1, 2, 0, 1, 2]]]
776
+ #
777
+ # b = Numo::NArray[[1, 2], [3, 4]]
778
+ # # => Numo::Int32#shape=[2,2]
779
+ # # [[1, 2],
780
+ # # [3, 4]]
781
+ #
782
+ # b.tile(2)
783
+ # # => Numo::Int32#shape=[2,4]
784
+ # # [[1, 2, 1, 2],
785
+ # # [3, 4, 3, 4]]
786
+ #
787
+ # b.tile(2,1)
788
+ # # => Numo::Int32#shape=[4,2]
789
+ # # [[1, 2],
790
+ # # [3, 4],
791
+ # # [1, 2],
792
+ # # [3, 4]]
793
+ #
794
+ # c = Numo::NArray[1,2,3,4]
795
+ # # => Numo::Int32#shape=[4]
796
+ # # [1, 2, 3, 4]
797
+ #
798
+ # c.tile(4,1)
799
+ # # => Numo::Int32#shape=[4,4]
800
+ # # [[1, 2, 3, 4],
801
+ # # [1, 2, 3, 4],
802
+ # # [1, 2, 3, 4],
803
+ # # [1, 2, 3, 4]]
804
+
805
+ def tile(*arg)
806
+ arg.each do |i|
807
+ raise ArgumentError, 'argument should be positive integer' if !i.is_a?(Integer) || i < 1
808
+ end
809
+ ns = arg.size
810
+ nd = ndim
811
+ shp = shape
812
+ new_shp = []
813
+ src_shp = []
814
+ res_shp = []
815
+ (nd - ns).times do
816
+ new_shp << 1
817
+ new_shp << (n = shp.shift)
818
+ src_shp << :new
819
+ src_shp << true
820
+ res_shp << n
821
+ end
822
+ (ns - nd).times do
823
+ new_shp << (m = arg.shift)
824
+ new_shp << 1
825
+ src_shp << :new
826
+ src_shp << :new
827
+ res_shp << m
828
+ end
829
+ [nd, ns].min.times do
830
+ new_shp << (m = arg.shift)
831
+ new_shp << (n = shp.shift)
832
+ src_shp << :new
833
+ src_shp << true
834
+ res_shp << (n * m)
835
+ end
836
+ self.class.new(*new_shp).store(self[*src_shp]).reshape(*res_shp)
837
+ end
838
+
839
+ # @example
840
+ # Numo::NArray[3].repeat(4)
841
+ # # => Numo::Int32#shape=[4]
842
+ # # [3, 3, 3, 3]
843
+ #
844
+ # x = Numo::NArray[[1,2],[3,4]]
845
+ # # => Numo::Int32#shape=[2,2]
846
+ # # [[1, 2],
847
+ # # [3, 4]]
848
+ #
849
+ # x.repeat(2)
850
+ # # => Numo::Int32#shape=[8]
851
+ # # [1, 1, 2, 2, 3, 3, 4, 4]
852
+ #
853
+ # x.repeat(3,axis:1)
854
+ # # => Numo::Int32#shape=[2,6]
855
+ # # [[1, 1, 1, 2, 2, 2],
856
+ # # [3, 3, 3, 4, 4, 4]]
857
+ #
858
+ # x.repeat([1,2],axis:0)
859
+ # # => Numo::Int32#shape=[3,2]
860
+ # # [[1, 2],
861
+ # # [3, 4],
862
+ # # [3, 4]]
863
+
864
+ def repeat(arg, axis: nil)
865
+ case axis
866
+ when Integer
867
+ axis = check_axis(axis)
868
+ c = self
869
+ when NilClass
870
+ c = flatten
871
+ axis = 0
872
+ else
873
+ raise ArgumentError, 'invalid axis'
874
+ end
875
+ case arg
876
+ when Integer
877
+ raise ArgumentError, 'argument should be positive integer' if !arg.is_a?(Integer) || arg < 1
878
+
879
+ idx = Array.new(c.shape[axis]) { |i| [i] * arg }.flatten
880
+ else
881
+ arg = arg.to_a
882
+ raise ArgumentError, 'repeat size shoud be equal to size along axis' if arg.size != c.shape[axis]
883
+
884
+ arg.each do |i|
885
+ raise ArgumentError, 'argument should be non-negative integer' if !i.is_a?(Integer) || i < 0
886
+ end
887
+ idx = arg.each_with_index.map { |a, i| [i] * a }.flatten
888
+ end
889
+ ref = [true] * c.ndim
890
+ ref[axis] = idx
891
+ c[*ref].copy
892
+ end
893
+
894
+ # Calculate the n-th discrete difference along given axis.
895
+ # @example
896
+ # x = Numo::DFloat[1, 2, 4, 7, 0]
897
+ # # => Numo::DFloat#shape=[5]
898
+ # # [1, 2, 4, 7, 0]
899
+ #
900
+ # x.diff
901
+ # # => Numo::DFloat#shape=[4]
902
+ # # [1, 2, 3, -7]
903
+ #
904
+ # x.diff(2)
905
+ # # => Numo::DFloat#shape=[3]
906
+ # # [1, 1, -10]
907
+ #
908
+ # x = Numo::DFloat[[1, 3, 6, 10], [0, 5, 6, 8]]
909
+ # # => Numo::DFloat#shape=[2,4]
910
+ # # [[1, 3, 6, 10],
911
+ # # [0, 5, 6, 8]]
912
+ #
913
+ # x.diff
914
+ # # => Numo::DFloat#shape=[2,3]
915
+ # # [[2, 3, 4],
916
+ # # [5, 1, 2]]
917
+ #
918
+ # x.diff(axis:0)
919
+ # # => Numo::DFloat#shape=[1,4]
920
+ # # [[-1, 2, 0, -2]]
921
+
922
+ def diff(n = 1, axis: -1)
923
+ axis = check_axis(axis)
924
+ raise ShapeError, "n=#{n} is invalid for shape[#{axis}]=#{shape[axis]}" if n < 0 || n >= shape[axis]
925
+
926
+ # calculate polynomial coefficient
927
+ c = self.class[-1, 1]
928
+ 2.upto(n) do |i|
929
+ x = self.class.zeros(i + 1)
930
+ x[0..-2] = c
931
+ y = self.class.zeros(i + 1)
932
+ y[1..-1] = c
933
+ c = y - x
934
+ end
935
+ s = [true] * ndim
936
+ s[axis] = n..-1
937
+ result = self[*s].dup
938
+ sum = result.inplace
939
+ (n - 1).downto(0) do |i|
940
+ s = [true] * ndim
941
+ s[axis] = i..(-n - 1 + i)
942
+ sum + (self[*s] * c[i]) # inplace addition
943
+ end
944
+ result
945
+ end
946
+
947
+ # Upper triangular matrix.
948
+ # Return a copy with the elements below the k-th diagonal filled with zero.
949
+ def triu(k = 0)
950
+ dup.triu!(k)
951
+ end
952
+
953
+ # Upper triangular matrix.
954
+ # Fill the self elements below the k-th diagonal with zero.
955
+ def triu!(k = 0)
956
+ raise NArray::ShapeError, 'must be >= 2-dimensional array' if ndim < 2
957
+
958
+ if contiguous?
959
+ *shp, m, n = shape
960
+ idx = tril_indices(k - 1)
961
+ reshape!(*shp, m * n)
962
+ self[false, idx] = 0
963
+ reshape!(*shp, m, n)
964
+ else
965
+ store(triu(k))
966
+ end
967
+ end
968
+
969
+ # Return the indices for the upper-triangle on and above the k-th diagonal.
970
+ def triu_indices(k = 0)
971
+ raise NArray::ShapeError, 'must be >= 2-dimensional array' if ndim < 2
972
+
973
+ m, n = shape[-2..]
974
+ NArray.triu_indices(m, n, k)
975
+ end
976
+
977
+ # Return the indices for the upper-triangle on and above the k-th diagonal.
978
+ def self.triu_indices(m, n, k = 0)
979
+ x = Numo::Int64.new(m, 1).seq + k
980
+ y = Numo::Int64.new(1, n).seq
981
+ (x <= y).where
982
+ end
983
+
984
+ # Lower triangular matrix.
985
+ # Return a copy with the elements above the k-th diagonal filled with zero.
986
+ def tril(k = 0)
987
+ dup.tril!(k)
988
+ end
989
+
990
+ # Lower triangular matrix.
991
+ # Fill the self elements above the k-th diagonal with zero.
992
+ def tril!(k = 0)
993
+ raise NArray::ShapeError, 'must be >= 2-dimensional array' if ndim < 2
994
+
995
+ if contiguous?
996
+ idx = triu_indices(k + 1)
997
+ *shp, m, n = shape
998
+ reshape!(*shp, m * n)
999
+ self[false, idx] = 0
1000
+ reshape!(*shp, m, n)
1001
+ else
1002
+ store(tril(k))
1003
+ end
1004
+ end
1005
+
1006
+ # Return the indices for the lower-triangle on and below the k-th diagonal.
1007
+ def tril_indices(k = 0)
1008
+ raise NArray::ShapeError, 'must be >= 2-dimensional array' if ndim < 2
1009
+
1010
+ m, n = shape[-2..]
1011
+ NArray.tril_indices(m, n, k)
1012
+ end
1013
+
1014
+ # Return the indices for the lower-triangle on and below the k-th diagonal.
1015
+ def self.tril_indices(m, n, k = 0)
1016
+ x = Numo::Int64.new(m, 1).seq + k
1017
+ y = Numo::Int64.new(1, n).seq
1018
+ (x >= y).where
1019
+ end
1020
+
1021
+ # Return the k-th diagonal indices.
1022
+ def diag_indices(k = 0)
1023
+ raise NArray::ShapeError, 'must be >= 2-dimensional array' if ndim < 2
1024
+
1025
+ m, n = shape[-2..]
1026
+ NArray.diag_indices(m, n, k)
1027
+ end
1028
+
1029
+ # Return the k-th diagonal indices.
1030
+ def self.diag_indices(m, n, k = 0)
1031
+ x = Numo::Int64.new(m, 1).seq + k
1032
+ y = Numo::Int64.new(1, n).seq
1033
+ (x.eq y).where
1034
+ end
1035
+
1036
+ # Return a matrix whose diagonal is constructed by self along the last axis.
1037
+ def diag(k = 0)
1038
+ *shp, n = shape
1039
+ n += k.abs
1040
+ a = self.class.zeros(*shp, n, n)
1041
+ a.diagonal(k).store(self)
1042
+ a
1043
+ end
1044
+
1045
+ # Return the sum along diagonals of the array.
1046
+ #
1047
+ # If 2-D array, computes the summation along its diagonal with the
1048
+ # given offset, i.e., sum of `a[i,i+offset]`.
1049
+ # If more than 2-D array, the diagonal is determined from the axes
1050
+ # specified by axis argument. The default is axis=[-2,-1].
1051
+ # @param offset [Integer] (optional, default=0) diagonal offset
1052
+ # @param axis [Array] (optional, default=[-2,-1]) diagonal axis
1053
+ # @param nan [Bool] (optional, default=false) nan-aware algorithm, i.e., if true then it ignores nan.
1054
+
1055
+ def trace(offset = nil, axis = nil, nan: false)
1056
+ diagonal(offset, axis).sum(nan: nan, axis: -1)
1057
+ end
1058
+
1059
+ @@warn_slow_dot = false
1060
+
1061
+ # Dot product of two arrays.
1062
+ # @param b [Numo::NArray]
1063
+ # @return [Numo::NArray] return dot product
1064
+
1065
+ def dot(b)
1066
+ t = self.class::UPCAST[b.class]
1067
+ if defined?(Linalg) && [SFloat, DFloat, SComplex, DComplex].include?(t)
1068
+ Linalg.dot(self, b)
1069
+ else
1070
+ b = self.class.asarray(b)
1071
+ case b.ndim
1072
+ when 1
1073
+ mulsum(b, axis: -1)
1074
+ else
1075
+ case ndim
1076
+ when 0
1077
+ b.mulsum(self, axis: -2)
1078
+ when 1
1079
+ self[true, :new].mulsum(b, axis: -2)
1080
+ else
1081
+ unless @@warn_slow_dot
1082
+ nx = 200
1083
+ ns = 200_000
1084
+ am, an = shape[-2..]
1085
+ bm, bn = b.shape[-2..]
1086
+ if am > nx && an > nx && bm > nx && bn > nx &&
1087
+ size > ns && b.size > ns
1088
+ @@warn_slow_dot = true
1089
+ warn "\nwarning: Built-in matrix dot is slow. Consider installing Numo::Linalg.\n\n"
1090
+ end
1091
+ end
1092
+ self[false, :new].mulsum(b[false, :new, true, true], axis: -2)
1093
+ end
1094
+ end
1095
+ end
1096
+ end
1097
+
1098
+ # Inner product of two arrays.
1099
+ # Same as `(a*b).sum(axis:-1)`.
1100
+ # @param b [Numo::NArray]
1101
+ # @param axis [Integer] applied axis
1102
+ # @return [Numo::NArray] return (a*b).sum(axis:axis)
1103
+
1104
+ def inner(b, axis: -1)
1105
+ mulsum(b, axis: axis)
1106
+ end
1107
+
1108
+ # Outer product of two arrays.
1109
+ # Same as `self[false,:new] * b[false,:new,true]`.
1110
+ #
1111
+ # @param b [Numo::NArray]
1112
+ # @param axis [Integer] applied axis (default=-1)
1113
+ # @return [Numo::NArray] return outer product
1114
+ # @example
1115
+ # a = Numo::DFloat.ones(5)
1116
+ # # => Numo::DFloat#shape=[5]
1117
+ # # [1, 1, 1, 1, 1]
1118
+ #
1119
+ # b = Numo::DFloat.linspace(-2,2,5)
1120
+ # # => Numo::DFloat#shape=[5]
1121
+ # # [-2, -1, 0, 1, 2]
1122
+ #
1123
+ # a.outer(b)
1124
+ # # => Numo::DFloat#shape=[5,5]
1125
+ # # [[-2, -1, 0, 1, 2],
1126
+ # # [-2, -1, 0, 1, 2],
1127
+ # # [-2, -1, 0, 1, 2],
1128
+ # # [-2, -1, 0, 1, 2],
1129
+ # # [-2, -1, 0, 1, 2]]
1130
+
1131
+ def outer(b, axis: nil)
1132
+ b = NArray.cast(b)
1133
+ if axis.nil?
1134
+ self[false, :new] * (b.ndim == 0 ? b : b[false, :new, true])
1135
+ else
1136
+ md, nd = [ndim, b.ndim].minmax
1137
+ axis = check_axis(axis) - nd
1138
+ raise ArgumentError, "axis=#{axis} is out of range" if axis < -md
1139
+
1140
+ adim = [true] * ndim
1141
+ adim[axis + ndim + 1, 0] = :new
1142
+ bdim = [true] * b.ndim
1143
+ bdim[axis + b.ndim, 0] = :new
1144
+ self[*adim] * b[*bdim]
1145
+ end
1146
+ end
1147
+
1148
+ # Percentile
1149
+ #
1150
+ # @param q [Numo::NArray]
1151
+ # @param axis [Integer] applied axis
1152
+ # @return [Numo::NArray] return percentile
1153
+ def percentile(q, axis: nil)
1154
+ raise ArgumentError, 'q is out of range' if q < 0 || q > 100
1155
+
1156
+ x = self
1157
+ unless axis
1158
+ axis = 0
1159
+ x = x.flatten
1160
+ end
1161
+
1162
+ sorted = x.sort(axis: axis)
1163
+ x = q / 100.0 * (sorted.shape[axis] - 1)
1164
+ r = x % 1
1165
+ i = x.floor
1166
+ refs = [true] * sorted.ndim
1167
+ refs[axis] = i
1168
+ if i == sorted.shape[axis] - 1
1169
+ sorted[*refs]
1170
+ else
1171
+ refs_upper = refs.dup
1172
+ refs_upper[axis] = i + 1
1173
+ sorted[*refs] + (r * (sorted[*refs_upper] - sorted[*refs]))
1174
+ end
1175
+ end
1176
+
1177
+ # Kronecker product of two arrays.
1178
+ #
1179
+ # kron(a,b)[k_0, k_1, ...] = a[i_0, i_1, ...] * b[j_0, j_1, ...]
1180
+ # where: k_n = i_n * b.shape[n] + j_n
1181
+ #
1182
+ # @param b [Numo::NArray]
1183
+ # @return [Numo::NArray] return Kronecker product
1184
+ # @example
1185
+ # Numo::DFloat[1,10,100].kron([5,6,7])
1186
+ # # => Numo::DFloat#shape=[9]
1187
+ # # [5, 6, 7, 50, 60, 70, 500, 600, 700]
1188
+ #
1189
+ # Numo::DFloat[5,6,7].kron([1,10,100])
1190
+ # # => Numo::DFloat#shape=[9]
1191
+ # # [5, 50, 500, 6, 60, 600, 7, 70, 700]
1192
+ #
1193
+ # Numo::DFloat.eye(2).kron(Numo::DFloat.ones(2,2))
1194
+ # # => Numo::DFloat#shape=[4,4]
1195
+ # # [[1, 1, 0, 0],
1196
+ # # [1, 1, 0, 0],
1197
+ # # [0, 0, 1, 1],
1198
+ # # [0, 0, 1, 1]]
1199
+
1200
+ def kron(b)
1201
+ b = NArray.cast(b)
1202
+ nda = ndim
1203
+ ndb = b.ndim
1204
+ shpa = shape
1205
+ shpb = b.shape
1206
+ adim = ([:new] * (2 * [ndb - nda, 0].max)) + ([true, :new] * nda)
1207
+ bdim = ([:new] * (2 * [nda - ndb, 0].max)) + ([:new, true] * ndb)
1208
+ shpr = (-[nda, ndb].max..-1).map { |i| (shpa[i] || 1) * (shpb[i] || 1) }
1209
+ (self[*adim] * b[*bdim]).reshape(*shpr)
1210
+ end
1211
+
1212
+ # under construction
1213
+ def cov(y = nil, ddof: 1, fweights: nil, aweights: nil)
1214
+ m = if y
1215
+ NArray.vstack([self, y])
1216
+ else
1217
+ self
1218
+ end
1219
+ w = nil
1220
+ if fweights
1221
+ f = fweights
1222
+ w = f
1223
+ end
1224
+ if aweights
1225
+ a = aweights
1226
+ w = w ? w * a : a
1227
+ end
1228
+ if w
1229
+ w_sum = w.sum(axis: -1, keepdims: true)
1230
+ if ddof == 0
1231
+ fact = w_sum
1232
+ elsif aweights.nil?
1233
+ fact = w_sum - ddof
1234
+ else
1235
+ wa_sum = (w * a).sum(axis: -1, keepdims: true)
1236
+ fact = w_sum - (ddof * wa_sum / w_sum)
1237
+ end
1238
+ raise StandardError, 'Degrees of freedom <= 0 for slice' if (fact <= 0).any?
1239
+ else
1240
+ fact = m.shape[-1] - ddof
1241
+ end
1242
+ if w
1243
+ m -= (m * w).sum(axis: -1, keepdims: true) / w_sum
1244
+ mw = m * w
1245
+ else
1246
+ m -= m.mean(axis: -1, keepdims: true)
1247
+ mw = m
1248
+ end
1249
+ mt = m.ndim < 2 ? m : m.swapaxes(-2, -1)
1250
+ mw.dot(mt.conj) / fact
1251
+ end
1252
+
1253
+ private
1254
+
1255
+ # @!visibility private
1256
+ def check_axis(axis)
1257
+ raise ArgumentError, "axis=#{axis} must be Integer" unless axis.is_a?(Integer)
1258
+
1259
+ a = axis
1260
+ a += ndim if a < 0
1261
+ raise ArgumentError, "axis=#{axis} is invalid" if a < 0 || a >= ndim
1262
+
1263
+ a
1264
+ end
1265
+ end
1266
+ end