-
-
Notifications
You must be signed in to change notification settings - Fork 779
/
core.pyx
3851 lines (3079 loc) · 118 KB
/
core.pyx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# distutils: language = c++
from __future__ import division
import ctypes
import sys
import numpy
import six
from cupy.core import flags
from cupy.cuda import stream
try:
from cupy.cuda import thrust
except ImportError:
pass
from cupy import util
cimport cpython
cimport cython
from libcpp cimport vector
from cupy.core cimport internal
from cupy.cuda cimport cublas
from cupy.cuda cimport function
from cupy.cuda cimport runtime
from cupy.cuda cimport memory
DEF MAX_NDIM = 25
@cython.profile(False)
cdef inline _should_use_rop(x, y):
xp = getattr(x, '__array_priority__', 0)
yp = getattr(y, '__array_priority__', 0)
return xp < yp and not isinstance(y, ndarray)
cdef class ndarray:
"""Multi-dimensional array on a CUDA device.
This class implements a subset of methods of :class:`numpy.ndarray`.
The difference is that this class allocates the array content on the
current GPU device.
Args:
shape (tuple of ints): Length of axes.
dtype: Data type. It must be an argument of :class:`numpy.dtype`.
memptr (cupy.cuda.MemoryPointer): Pointer to the array content head.
strides (tuple of ints): The strides for axes.
order ({'C', 'F'}): Row-major (C-style) or column-major
(Fortran-style) order.
Attributes:
base (None or cupy.ndarray): Base array from which this array is
created as a view.
data (cupy.cuda.MemoryPointer): Pointer to the array content head.
dtype(numpy.dtype): Dtype object of element type.
.. seealso::
`Data type objects (dtype) \
<http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html>`_
size (int): Number of elements this array holds.
This is equivalent to product over the shape tuple.
.. seealso:: :attr:`numpy.ndarray.size`
"""
def __init__(self, shape, dtype=float, memptr=None, order='C'):
cdef Py_ssize_t x
self._shape = internal.get_size(shape)
for x in self._shape:
if x < 0:
raise ValueError('Negative dimensions are not allowed')
self.dtype = numpy.dtype(dtype)
self.size = internal.prod_ssize_t(self._shape)
if memptr is None:
self.data = memory.alloc(self.size * self.dtype.itemsize)
else:
self.data = memptr
self.base = None
if order == 'C':
self._strides = internal.get_contiguous_strides(
self._shape, self.itemsize, is_c_contiguous=True)
self._c_contiguous = True
self._update_f_contiguity()
elif order == 'F':
self._strides = internal.get_contiguous_strides(
self._shape, self.itemsize, is_c_contiguous=False)
self._f_contiguous = True
self._update_c_contiguity()
else:
raise TypeError('order not understood')
# The definition order of attributes and methods are borrowed from the
# order of documentation at the following NumPy document.
# http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html
# -------------------------------------------------------------------------
# Memory layout
# -------------------------------------------------------------------------
@property
def flags(self):
"""Object containing memory-layout information.
It only contains ``c_contiguous``, ``f_contiguous``, and ``owndata``
attributes. All of these are read-only. Accessing by indexes is also
supported.
.. seealso:: :attr:`numpy.ndarray.flags`
"""
return flags.Flags(self._c_contiguous, self._f_contiguous,
self.base is None)
property shape:
"""Lengths of axes.
Setter of this property involves reshaping without copy. If the array
cannot be reshaped without copy, it raises an exception.
.. seealso: :attr:`numpy.ndarray.shape`
"""
def __get__(self):
return tuple(self._shape)
def __set__(self, newshape):
cdef vector.vector[Py_ssize_t] shape, strides
if not cpython.PySequence_Check(newshape):
newshape = (newshape,)
shape = internal.infer_unknown_dimension(newshape, self.size)
strides = _get_strides_for_nocopy_reshape(self, shape)
if strides.size() != shape.size():
raise AttributeError('incompatible shape')
self._shape = shape
self._strides = strides
self._update_f_contiguity()
@property
def strides(self):
"""Strides of axes in bytes.
.. seealso:: :attr:`numpy.ndarray.strides`
"""
return tuple(self._strides)
@property
def ndim(self):
"""Number of dimensions.
``a.ndim`` is equivalent to ``len(a.shape)``.
.. seealso:: :attr:`numpy.ndarray.ndim`
"""
return self._shape.size()
@property
def itemsize(self):
"""Size of each element in bytes.
.. seealso:: :attr:`numpy.ndarray.itemsize`
"""
return self.dtype.itemsize
@property
def nbytes(self):
"""Size of whole elements in bytes.
It does not count skips between elements.
.. seealso:: :attr:`numpy.ndarray.nbytes`
"""
return self.size * self.dtype.itemsize
# -------------------------------------------------------------------------
# Other attributes
# -------------------------------------------------------------------------
@property
def T(self):
"""Shape-reversed view of the array.
If ndim < 2, then this is just a reference to the array itself.
"""
if self.ndim < 2:
return self
else:
return self._transpose(vector.vector[Py_ssize_t]())
__array_priority__ = 100
# -------------------------------------------------------------------------
# Array interface
# -------------------------------------------------------------------------
# TODO(beam2d): Implement __array_interface__
# -------------------------------------------------------------------------
# foreign function interface
# -------------------------------------------------------------------------
@property
def cstruct(self):
"""C representation of the array.
This property is used for sending an array to CUDA kernels. The type of
returned C structure is different for different dtypes and ndims. The
definition of C type is written in ``cupy/carray.cuh``.
"""
return CArray(self)
# -------------------------------------------------------------------------
# Array conversion
# -------------------------------------------------------------------------
# TODO(okuta): Implement item
cpdef tolist(self):
"""Converts the array to a (possibly nested) Python list.
Returns:
list: The possibly nested Python list of array elements.
.. seealso:: :meth:`numpy.ndarray.tolist`
"""
return self.get().tolist()
# TODO(okuta): Implement itemset
# TODO(okuta): Implement tostring
# TODO(okuta): Implement tobytes
cpdef tofile(self, fid, sep='', format='%s'):
"""Writes the array to a file.
.. seealso:: :meth:`numpy.ndarray.tolist`
"""
self.get().tofile(fid, sep, format)
cpdef dump(self, file):
"""Dumps a pickle of the array to a file.
Dumped file can be read back to :class:`cupy.ndarray` by
:func:`cupy.load`.
"""
six.moves.cPickle.dump(self, file, -1)
cpdef dumps(self):
"""Dumps a pickle of the array to a string."""
return six.moves.cPickle.dumps(self, -1)
cpdef ndarray astype(self, dtype, copy=True):
"""Casts the array to given data type.
Args:
dtype: Type specifier.
copy (bool): If it is False and no cast happens, then this method
returns the array itself. Otherwise, a copy is returned.
Returns:
If ``copy`` is False and no cast is required, then the array itself
is returned. Otherwise, it returns a (possibly casted) copy of the
array.
.. note::
This method currently does not support ``order``, ``casting``, and
``subok`` arguments.
.. seealso:: :meth:`numpy.ndarray.astype`
"""
# TODO(beam2d): Support ordering, casting, and subok option
dtype = numpy.dtype(dtype)
if dtype.type == self.dtype.type:
if copy:
return self.copy()
else:
return self
else:
newarray = ndarray(self.shape, dtype=dtype)
elementwise_copy(self, newarray)
return newarray
# TODO(okuta): Implement byteswap
cpdef ndarray copy(self, order='C'):
"""Returns a copy of the array.
Args:
order ({'C', 'F'}): Row-major (C-style) or column-major
(Fortran-style) order. This function currently does not
support order 'A' and 'K'.
.. seealso::
:func:`cupy.copy` for full documentation,
:meth:`numpy.ndarray.copy`
"""
cdef ndarray a, newarray
# TODO(beam2d): Support ordering option 'A' and 'K'
if order not in ['C', 'F']:
raise TypeError('order not understood')
if self.size == 0:
return ndarray(self.shape, self.dtype, order=order)
a = self
if order == 'C' and not self._c_contiguous:
with self.device:
a = ascontiguousarray(self)
if a.data.device.id == device.get_device_id():
return a
elif order == 'F' and not self._f_contiguous:
with self.device:
a = asfortranarray(self)
if a.data.device.id == device.get_device_id():
return a
newarray = ndarray(a.shape, a.dtype, order=order)
newarray.data.copy_from_device(a.data, a.nbytes)
return newarray
cpdef ndarray view(self, dtype=None):
"""Returns a view of the array.
Args:
dtype: If this is different from the data type of the array, the
returned view reinterpret the memory sequence as an array of
this type.
Returns:
cupy.ndarray: A view of the array. A reference to the original
array is stored at the :attr:`~ndarray.base` attribute.
.. seealso:: :meth:`numpy.ndarray.view`
"""
# Use __new__ instead of __init__ to skip recomputation of contiguity
cdef ndarray v
v = ndarray.__new__(ndarray)
v.size = self.size
v._shape = self._shape
v._strides = self._strides
v._c_contiguous = self._c_contiguous
v._f_contiguous = self._f_contiguous
v.dtype = self.dtype if dtype is None else numpy.dtype(dtype)
v.data = self.data
v.base = self.base if self.base is not None else self
return v
# TODO(okuta): Implement getfield
# TODO(okuta): Implement setflags
cpdef fill(self, value):
"""Fills the array with a scalar value.
Args:
value: A scalar value to fill the array content.
.. seealso:: :meth:`numpy.ndarray.fill`
"""
if isinstance(value, numpy.ndarray):
if value.shape != ():
raise ValueError(
'non-scalar numpy.ndarray cannot be used for fill')
value = value.item()
if value == 0 and self._c_contiguous:
self.data.memset_async(0, self.nbytes, stream.Stream(True))
else:
elementwise_copy(value, self, dtype=self.dtype)
# -------------------------------------------------------------------------
# Shape manipulation
# -------------------------------------------------------------------------
cpdef ndarray _reshape(self, vector.vector[Py_ssize_t] shape):
cdef vector.vector[Py_ssize_t] strides
cdef ndarray newarray
shape = internal.infer_unknown_dimension(shape, self.size)
if internal.vector_equal(shape, self._shape):
return self.view()
strides = _get_strides_for_nocopy_reshape(self, shape)
if strides.size() == shape.size():
newarray = self.view()
else:
newarray = self.copy()
strides = _get_strides_for_nocopy_reshape(newarray, shape)
if shape.size() != strides.size():
raise ValueError('total size of new array must be unchanged')
newarray._set_shape_and_strides(shape, strides, False)
return newarray
def reshape(self, *shape):
"""Returns an array of a different shape and the same content.
.. seealso::
:func:`cupy.reshape` for full documentation,
:meth:`numpy.ndarray.reshape`
"""
# TODO(beam2d): Support ordering option
if len(shape) == 1 and cpython.PySequence_Check(shape[0]):
shape = shape[0]
return self._reshape(shape)
# TODO(okuta): Implement resize
cpdef ndarray _transpose(self, vector.vector[Py_ssize_t] axes):
cdef ndarray ret
cdef vector.vector[Py_ssize_t] a_axes, rev_axes
cdef Py_ssize_t ndim, axis
ndim = self._shape.size()
ret = self.view()
if axes.size() == 0:
ret._shape.assign(self._shape.rbegin(), self._shape.rend())
ret._strides.assign(self._strides.rbegin(), self._strides.rend())
ret._c_contiguous = self._f_contiguous
ret._f_contiguous = self._c_contiguous
return ret
if <Py_ssize_t>axes.size() != ndim:
raise ValueError('Invalid axes value: %s' % str(axes))
for i in range(ndim):
a_axes.push_back(i)
axis = axes[i]
if axis < -ndim or axis >= ndim:
raise IndexError('Axes overrun')
axes[i] = axis % ndim
if internal.vector_equal(a_axes, axes):
return ret
rev_axes.assign(axes.rbegin(), axes.rend())
if internal.vector_equal(a_axes, rev_axes):
ret._shape.assign(self._shape.rbegin(), self._shape.rend())
ret._strides.assign(self._strides.rbegin(), self._strides.rend())
ret._c_contiguous = self._f_contiguous
ret._f_contiguous = self._c_contiguous
return ret
if ndim != len({i for i in axes}):
raise ValueError('Invalid axes value: %s' % str(axes))
ret._shape.clear()
ret._strides.clear()
for axis in axes:
ret._shape.push_back(self._shape[axis])
ret._strides.push_back(self._strides[axis])
ret._update_contiguity()
return ret
def transpose(self, *axes):
"""Returns a view of the array with axes permuted.
.. seealso::
:func:`cupy.transpose` for full documentation,
:meth:`numpy.ndarray.reshape`
"""
cdef ndarray ret
cdef vector.vector[Py_ssize_t] vec_axes, a_axes, temp_axes
cdef Py_ssize_t ndim, axis
if len(axes) == 1:
a = axes[0]
if a is None:
axes = ()
elif cpython.PySequence_Check(a):
axes = a
return self._transpose(axes)
cpdef ndarray swapaxes(self, Py_ssize_t axis1, Py_ssize_t axis2):
"""Returns a view of the array with two axes swapped.
.. seealso::
:func:`cupy.swapaxes` for full documentation,
:meth:`numpy.ndarray.swapaxes`
"""
cdef Py_ssize_t ndim=self.ndim
cdef vector.vector[Py_ssize_t] axes
if axis1 < -ndim or axis1 >= ndim or axis2 < -ndim or axis2 >= ndim:
raise ValueError('Axis out of range')
axis1 %= ndim
axis2 %= ndim
for i in range(ndim):
axes.push_back(i)
axes[axis1], axes[axis2] = axes[axis2], axes[axis1]
return self._transpose(axes)
cpdef ndarray flatten(self):
"""Returns a copy of the array flatten into one dimension.
It currently supports C-order only.
Returns:
cupy.ndarray: A copy of the array with one dimension.
.. seealso:: :meth:`numpy.ndarray.flatten`
"""
# TODO(beam2d): Support ordering option
if self._c_contiguous:
newarray = self.copy()
else:
newarray = ndarray(self.shape, self.dtype)
elementwise_copy(self, newarray)
newarray._shape.assign(<Py_ssize_t>1, self.size)
newarray._strides.assign(<Py_ssize_t>1, <Py_ssize_t>self.itemsize)
newarray._c_contiguous = True
newarray._f_contiguous = True
return newarray
cpdef ndarray ravel(self):
"""Returns an array flattened into one dimension.
.. seealso::
:func:`cupy.ravel` for full documentation,
:meth:`numpy.ndarray.ravel`
"""
# TODO(beam2d): Support ordering option
cdef vector.vector[Py_ssize_t] shape
shape.push_back(self.size)
return self._reshape(shape)
cpdef ndarray squeeze(self, axis=None):
"""Returns a view with size-one axes removed.
.. seealso::
:func:`cupy.squeeze` for full documentation,
:meth:`numpy.ndarray.squeeze`
"""
cdef vector.vector[char] axis_flags
cdef vector.vector[Py_ssize_t] newshape, newstrides
cdef Py_ssize_t ndim, naxes, _axis
ndim = self._shape.size()
axis_flags = vector.vector[char](ndim, 0)
# Convert axis to boolean flag.
if axis is None:
for idim in range(ndim):
if self._shape[idim] == 1:
axis_flags[idim] = 1
elif isinstance(axis, tuple):
naxes = <Py_ssize_t>len(axis)
for i in range(naxes):
_axis = <Py_ssize_t>axis[i]
axis_orig = _axis
if _axis < 0:
_axis += ndim
if _axis < 0 or _axis >= ndim:
msg = "'axis' entry %d is out of bounds [-%d, %d)"
raise ValueError(msg % (axis_orig, ndim, ndim))
if axis_flags[_axis] == 1:
raise ValueError("duplicate value in 'axis'")
axis_flags[_axis] = 1
else:
_axis = <Py_ssize_t>axis
axis_orig = _axis
if _axis < 0:
_axis += ndim
if ndim == 0 and (_axis == 0 or _axis == -1):
# Special case letting axis={-1,0} slip through for scalars,
# for backwards compatibility reasons.
pass
else:
if _axis < 0 or _axis >= ndim:
msg = "'axis' entry %d is out of bounds [-%d, %d)"
raise ValueError(msg % (axis_orig, ndim, ndim))
axis_flags[_axis] = 1
# Verify that the axes requested are all of size one
any_ones = 0
for idim in range(ndim):
if axis_flags[idim] != 0:
if self._shape[idim] == 1:
any_ones = 1
else:
raise ValueError('cannot select an axis to squeeze out '
'which has size not equal to one')
# If there were no axes to squeeze out, return the same array
if any_ones == 0:
return self
for i in range(ndim):
if axis_flags[i] == 0:
newshape.push_back(self._shape[i])
newstrides.push_back(self._strides[i])
v = self.view()
v._set_shape_and_strides(newshape, newstrides, False)
return v
# -------------------------------------------------------------------------
# Item selection and manipulation
# -------------------------------------------------------------------------
cpdef ndarray take(self, indices, axis=None, out=None):
"""Returns an array of elements at given indices along the axis.
.. seealso::
:func:`cupy.take` for full documentation,
:meth:`numpy.ndarray.take`
"""
return _take(self, indices, li=axis, ri=axis, out=out)
# TODO(okuta): Implement put
cpdef repeat(self, repeats, axis=None):
"""Returns an array with repeated arrays along an axis.
.. seealso::
:func:`cupy.repeat` for full documentation,
:meth:`numpy.ndarray.repeat`
"""
return _repeat(self, repeats, axis)
cpdef choose(self, choices, out=None, mode='raise'):
a = self
n = choices.shape[0]
# broadcast `a` and `choices[i]` for all i
if a.ndim < choices.ndim - 1:
for i in range(choices.ndim - 1 - a.ndim):
a = a[None, ...]
elif a.ndim > choices.ndim - 1:
for i in range(a.ndim + 1 - choices.ndim):
choices = choices[:, None, ...]
ba, bcs = broadcast(a, choices).values
if out is None:
out = ndarray(ba.shape[1:], choices.dtype)
n_channel = numpy.prod(bcs[0].shape)
if mode == 'raise':
if not ((a < n).all() and (0 <= a).all()):
raise ValueError('invalid entry in choice array')
_choose_kernel(ba[0], bcs, n_channel, out)
elif mode == 'wrap':
ba = ba[0] % n
_choose_kernel(ba, bcs, n_channel, out)
elif mode == 'clip':
_choose_clip_kernel(ba[0], bcs, n_channel, n, out)
else:
raise TypeError('clipmode not understood')
return out
def sort(self):
"""Sort an array, in-place with a stable sorting algorithm.
.. note::
For its implementation reason, ``ndarray.sort`` currently supports
only arrays with their rank of one and their own data, and does not
support ``axis``, ``kind`` and ``order`` parameters that
``numpy.ndarray.sort`` does support.
.. seealso::
:func:`cupy.sort` for full documentation,
:meth:`numpy.ndarray.sort`
"""
# TODO(takagi): Support axis argument.
# TODO(takagi): Support kind argument.
if self.ndim == 0:
msg = 'Sorting arrays with the rank of zero is not supported'
raise ValueError(msg)
# TODO(takagi): Support ranks of two or more
if self.ndim > 1:
msg = ('Sorting arrays with the rank of two or more is '
'not supported')
raise ValueError(msg)
# TODO(takagi): Support sorting views
if not self._c_contiguous:
raise ValueError('Sorting non-contiguous array is not supported.')
# TODO(takagi): Support float16 and bool
try:
thrust.sort(self.dtype, self.data.ptr, self._shape[0])
except NameError:
msg = ('Thrust is needed to use cupy.sort. Please install CUDA '
'Toolkit with Thrust then reinstall Chainer after '
'uninstalling it.')
raise RuntimeError(msg)
def argsort(self):
"""Return the indices that would sort an array with stable sorting
.. note::
For its implementation reason, ``ndarray.argsort`` currently
supports only arrays with their rank of one, and does not support
``axis``, ``kind`` and ``order`` parameters that
``numpy.ndarray.argsort`` supports.
.. seealso::
:func:`cupy.argsort` for full documentation,
:meth:`numpy.ndarray.argsort`
"""
# TODO(takagi): Support axis argument.
# TODO(takagi): Support kind argument.
if self.ndim == 0:
msg = 'Sorting arrays with the rank of zero is not supported'
raise ValueError(msg)
# TODO(takagi): Support ranks of two or more
if self.ndim > 1:
msg = ('Sorting arrays with the rank of two or more is '
'not supported')
raise ValueError(msg)
# Assuming that Py_ssize_t can be represented with numpy.int64.
assert cython.sizeof(Py_ssize_t) == 8
idx_array = ndarray(self.shape, dtype=numpy.int64)
# TODO(takagi): Support float16 and bool
try:
thrust.argsort(
self.dtype, idx_array.data.ptr, self.data.ptr, self._shape[0])
except NameError:
msg = ('Thrust is needed to use cupy.argsort. Please install CUDA '
'Toolkit with Thrust then reinstall CuPy after '
'uninstalling it.')
raise RuntimeError(msg)
return idx_array
# TODO(okuta): Implement partition
# TODO(okuta): Implement argpartition
# TODO(okuta): Implement searchsorted
def nonzero(self):
"""Return the indices of the elements that are non-zero.
Returned Array is containing the indices of the non-zero elements
in that dimension.
Returns:
tuple of arrays: Indices of elements that are non-zero.
.. seealso::
:func:`numpy.nonzero`
"""
condition = self != 0
dtype = numpy.int64
scan_index = scan(condition.astype(dtype).ravel())
count_nonzero = int(scan_index[-1])
if self.ndim <= 1:
dst = ndarray((count_nonzero,), dtype=dtype)
kern = _nonzero_1d_kernel(self.dtype, dtype)
kern.linear_launch(self.size, (self.ravel(), scan_index, dst))
return dst,
else:
dst = ndarray((count_nonzero * self.ndim,), dtype=dtype)
kern = _nonzero_kernel(self.dtype, self.ndim, dtype, dtype)
kern.linear_launch(self.size,
(self.ravel(), Indexer(self.shape),
scan_index, dst))
return tuple([dst[i::self.ndim]
for i in six.moves.range(self.ndim)])
# TODO(okuta): Implement compress
cpdef ndarray diagonal(self, offset=0, axis1=0, axis2=1):
"""Returns a view of the specified diagonals.
.. seealso::
:func:`cupy.diagonal` for full documentation,
:meth:`numpy.ndarray.diagonal`
"""
return _diagonal(self, offset, axis1, axis2)
# -------------------------------------------------------------------------
# Calculation
# -------------------------------------------------------------------------
cpdef ndarray max(self, axis=None, out=None, dtype=None, keepdims=False):
"""Returns the maximum along a given axis.
.. seealso::
:func:`cupy.amax` for full documentation,
:meth:`numpy.ndarray.max`
"""
return _amax(
self, axis=axis, out=out, dtype=dtype, keepdims=keepdims)
cpdef ndarray argmax(self, axis=None, out=None, dtype=None,
keepdims=False):
"""Returns the indices of the maximum along a given axis.
.. seealso::
:func:`cupy.argmax` for full documentation,
:meth:`numpy.ndarray.argmax`
"""
return _argmax(
self, axis=axis, out=out, dtype=dtype, keepdims=keepdims)
cpdef ndarray min(self, axis=None, out=None, dtype=None, keepdims=False):
"""Returns the minimum along a given axis.
.. seealso::
:func:`cupy.amin` for full documentation,
:meth:`numpy.ndarray.min`
"""
return _amin(
self, axis=axis, out=out, dtype=dtype, keepdims=keepdims)
cpdef ndarray argmin(self, axis=None, out=None, dtype=None,
keepdims=False):
"""Returns the indices of the minimum along a given axis.
.. seealso::
:func:`cupy.argmin` for full documentation,
:meth:`numpy.ndarray.argmin`
"""
return _argmin(
self, axis=axis, out=out, dtype=dtype, keepdims=keepdims)
# TODO(okuta): Implement ptp
cpdef ndarray clip(self, a_min, a_max, out=None):
"""Returns an array with values limited to [a_min, a_max].
.. seealso::
:func:`cupy.clip` for full documentation,
:meth:`numpy.ndarray.clip`
"""
return _clip(self, a_min, a_max, out=out)
# TODO(okuta): Implement round
cpdef ndarray trace(self, offset=0, axis1=0, axis2=1, dtype=None,
out=None):
"""Returns the sum along diagonals of the array.
.. seealso::
:func:`cupy.trace` for full documentation,
:meth:`numpy.ndarray.trace`
"""
d = self.diagonal(offset, axis1, axis2)
return d.sum(-1, dtype, out, False)
cpdef ndarray sum(self, axis=None, dtype=None, out=None, keepdims=False):
"""Returns the sum along a given axis.
.. seealso::
:func:`cupy.sum` for full documentation,
:meth:`numpy.ndarray.sum`
"""
return _sum(self, axis, dtype, out, keepdims)
# TODO(okuta): Implement cumsum
cpdef ndarray mean(self, axis=None, dtype=None, out=None, keepdims=False):
"""Returns the mean along a given axis.
.. seealso::
:func:`cupy.mean` for full documentation,
:meth:`numpy.ndarray.mean`
"""
return _mean(self, axis=axis, dtype=dtype, out=out, keepdims=keepdims)
cpdef ndarray var(self, axis=None, dtype=None, out=None, ddof=0,
keepdims=False):
"""Returns the variance along a given axis.
.. seealso::
:func:`cupy.var` for full documentation,
:meth:`numpy.ndarray.var`
"""
return _var(self, axis=axis, dtype=dtype, out=out, keepdims=keepdims)
cpdef ndarray std(self, axis=None, dtype=None, out=None, ddof=0,
keepdims=False):
"""Returns the standard deviation along a given axis.
.. seealso::
:func:`cupy.std` for full documentation,
:meth:`numpy.ndarray.std`
"""
return _std(self, axis=axis, dtype=dtype, out=out, keepdims=keepdims)
cpdef ndarray prod(self, axis=None, dtype=None, out=None, keepdims=None):
"""Returns the product along a given axis.
.. seealso::
:func:`cupy.prod` for full documentation,
:meth:`numpy.ndarray.prod`
"""
return _prod(self, axis, dtype, out, keepdims)
# TODO(okuta): Implement cumprod
cpdef ndarray all(self, axis=None, out=None, keepdims=False):
return _all(self, axis=axis, out=out, keepdims=keepdims)
cpdef ndarray any(self, axis=None, out=None, keepdims=False):
return _any(self, axis=axis, out=out, keepdims=keepdims)
# -------------------------------------------------------------------------
# Arithmetic and comparison operations
# -------------------------------------------------------------------------
# Comparison operators:
def __richcmp__(object self, object other, int op):
if op == 0:
return less(self, other)
if op == 1:
return less_equal(self, other)
if op == 2:
return equal(self, other)
if op == 3:
return not_equal(self, other)
if op == 4:
return greater(self, other)
if op == 5:
return greater_equal(self, other)
return NotImplemented
# Truth value of an array (bool):
def __nonzero__(self):
if self.size == 0:
return False
elif self.size == 1:
return bool(self.get())
else:
msg = ('The truth value of an array with more than one element is '
'ambiguous. Use a.any() or a.all()')
raise ValueError(msg)
# Unary operations:
def __neg__(self):
return negative(self)
def __pos__(self):
return self
def __abs__(self):
return absolute(self)
def __invert__(self):
return invert(self)
# Arithmetic:
def __add__(x, y):
if _should_use_rop(x, y):
return y.__radd__(x)
else:
return add(x, y)
def __sub__(x, y):
if _should_use_rop(x, y):
return y.__rsub__(x)