From cd455237f09628ff09bca2c1488df5d93f8abf36 Mon Sep 17 00:00:00 2001 From: Ryosuke Okuta Date: Mon, 19 Jun 2017 15:06:58 +0900 Subject: [PATCH 1/2] Add order and subok argument --- cupy/core/core.pyx | 28 ++++++---- cupy/creation/from_data.py | 22 ++++++-- .../creation_tests/test_from_data.py | 52 ++++++++++++++----- 3 files changed, 74 insertions(+), 28 deletions(-) diff --git a/cupy/core/core.pyx b/cupy/core/core.pyx index e084e32677b..276dc8c03d6 100644 --- a/cupy/core/core.pyx +++ b/cupy/core/core.pyx @@ -269,9 +269,9 @@ cdef class ndarray: dtype: Type specifier. order ({'C', 'F', 'A', 'K'}): Row-major (C-style) or column-major (Fortran-style) order. - When `order` is 'A', it uses 'F' if `a` is column-major and - uses `C` otherwise. - And when `order` is 'K', it keeps strides as closely as + When ``order`` is 'A', it uses 'F' if ``a`` is column-major and + uses 'C' otherwise. + And when ``order`` is 'K', it keeps strides as closely as possible. copy (bool): If it is False and no cast happens, then this method returns the array itself. Otherwise, a copy is returned. @@ -349,8 +349,8 @@ cdef class ndarray: Args: order ({'C', 'F', 'A', 'K'}): Row-major (C-style) or column-major (Fortran-style) order. - When `order` is 'A', it uses 'F' if `a` is column-major and - uses `C` otherwise. + When ``order`` is 'A', it uses 'F' if ``a`` is column-major and + uses 'C' otherwise. And when `order` is 'K', it keeps strides as closely as possible. @@ -1891,19 +1891,22 @@ cdef _argmax = create_reduction_func( # Array creation routines # ----------------------------------------------------------------------------- -cpdef ndarray array(obj, dtype=None, bint copy=True, Py_ssize_t ndmin=0): - # TODO(beam2d): Support order and subok options +cpdef ndarray array(obj, dtype=None, bint copy=True, str order='K', + bint subok=False, Py_ssize_t ndmin=0): + # TODO(beam2d): Support subok options cdef Py_ssize_t nvidem cdef ndarray a, src + if subok: + raise NotImplementedError if isinstance(obj, ndarray): src = obj if dtype is None: dtype = src.dtype dev = src.data.device if dev is None or dev.id == device.get_device_id(): - a = src.astype(dtype, copy=copy) + a = src.astype(dtype, order=order, copy=copy) else: - a = src.copy().astype(dtype, copy=False) + a = src.copy(order=order).astype(dtype, copy=False) ndim = a._shape.size() if ndmin > ndim: @@ -1912,12 +1915,15 @@ cpdef ndarray array(obj, dtype=None, bint copy=True, Py_ssize_t ndmin=0): a = a.view() a.shape = (1,) * (ndmin - ndim) + a.shape else: - a_cpu = numpy.array(obj, dtype=dtype, copy=False, order='C', + if order == 'K': + order = 'A' + a_cpu = numpy.array(obj, dtype=dtype, copy=False, order=order, ndmin=ndmin) + order = 'C' if a_cpu.flags.c_contiguous else 'F' a_dtype = a_cpu.dtype if a_dtype.char not in '?bhilqBHILQefd': raise ValueError('Unsupported dtype %s' % a_dtype) - a = ndarray(a_cpu.shape, dtype=a_dtype) + a = ndarray(a_cpu.shape, dtype=a_dtype, order=order) if a_cpu.ndim == 0: a.fill(a_cpu[()]) return a diff --git a/cupy/creation/from_data.py b/cupy/creation/from_data.py index cd3a76ebd78..f35ff307124 100644 --- a/cupy/creation/from_data.py +++ b/cupy/creation/from_data.py @@ -1,7 +1,7 @@ from cupy import core -def array(obj, dtype=None, copy=True, ndmin=0): +def array(obj, dtype=None, copy=True, order='K', subok=False, ndmin=0): """Creates an array on the current device. This function currently does not support the ``order`` and ``subok`` @@ -13,17 +13,33 @@ def array(obj, dtype=None, copy=True, ndmin=0): dtype: Data type specifier. copy (bool): If ``False``, this function returns ``obj`` if possible. Otherwise this function always returns a new array. + order ({'C', 'F', 'A', 'K'}): Row-major (C-style) or column-major + (Fortran-style) order. + When ``order`` is 'A', it uses 'F' if ``a`` is column-major and + uses 'C' otherwise. + And when ``order`` is 'K', it keeps strides as closely as + possible. + If ``obj`` is ``numpy.ndarray``, thi function returns 'C' or 'F' + order array. + subok (bool): If True, then sub-classes will be passed-through, + otherwise the returned array will be forced to be a base-class + array (default). ndmin (int): Minimum number of dimensions. Ones are inserted to the head of the shape if needed. Returns: cupy.ndarray: An array on the current device. + + + .. note:: + This method currently does not support ``subok`` + arguments. + .. seealso:: :func:`numpy.array` """ - # TODO(beam2d): Support order and subok options - return core.array(obj, dtype, copy, ndmin) + return core.array(obj, dtype, copy, order, subok, ndmin) def asarray(a, dtype=None): diff --git a/tests/cupy_tests/creation_tests/test_from_data.py b/tests/cupy_tests/creation_tests/test_from_data.py index 2ff5a1aa53f..583a4759e97 100644 --- a/tests/cupy_tests/creation_tests/test_from_data.py +++ b/tests/cupy_tests/creation_tests/test_from_data.py @@ -11,51 +11,75 @@ class TestFromData(unittest.TestCase): _multiprocess_can_split_ = True + @testing.for_orders('CFAK') @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() - def test_array(self, xp, dtype): - return xp.array([[1, 2, 3], [2, 3, 4]], dtype=dtype) + def test_array(self, xp, dtype, order): + return xp.array([[1, 2, 3], [2, 3, 4]], dtype=dtype, order=order) + @testing.for_orders('CFAK') @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() - def test_array_from_numpy(self, xp, dtype): + def test_array_from_numpy(self, xp, dtype, order): a = testing.shaped_arange((2, 3, 4), numpy, dtype) - return xp.array(a) + return xp.array(a, order=order) + @testing.for_orders('CFAK') @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() - def test_array_copy(self, xp, dtype): + def test_array_copy(self, xp, dtype, order): a = testing.shaped_arange((2, 3, 4), xp, dtype) - return xp.array(a) + return xp.array(a, order=order) + @testing.for_orders('CFAK') @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() - def test_array_copy_is_copied(self, xp, dtype): + def test_array_copy_is_copied(self, xp, dtype, order): a = testing.shaped_arange((2, 3, 4), xp, dtype) - b = xp.array(a) + b = xp.array(a, order=order) a.fill(0) return b + @testing.for_orders('CFAK') @testing.for_all_dtypes(name='dtype1') @testing.for_all_dtypes(name='dtype2') @testing.numpy_cupy_array_equal() - def test_array_copy_with_dtype(self, xp, dtype1, dtype2): + def test_array_copy_with_dtype(self, xp, dtype1, dtype2, order): a = testing.shaped_arange((2, 3, 4), xp, dtype1) - return xp.array(a, dtype=dtype2) + return xp.array(a, dtype=dtype2, order=order) + @testing.for_orders('CFAK') @testing.numpy_cupy_array_equal() - def test_array_copy_with_dtype_being_none(self, xp): + def test_array_copy_with_dtype_being_none(self, xp, order): a = testing.shaped_arange((2, 3, 4), xp) - return xp.array(a, dtype=None) + return xp.array(a, dtype=None, order=order) + @testing.for_orders('CFAK') @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() - def test_array_no_copy(self, xp, dtype): + def test_array_no_copy(self, xp, dtype, order): a = testing.shaped_arange((2, 3, 4), xp, dtype) - b = xp.array(a, copy=False) + b = xp.array(a, copy=False, order=order) a.fill(0) return b + @testing.for_orders('CFAK') + @testing.for_all_dtypes() + @testing.numpy_cupy_array_equal() + def test_array_f_contiguous_input(self, xp, dtype, order): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + a = xp.asfortranarray(a) + b = xp.array(a, copy=False, order=order) + return b + + @testing.for_all_dtypes() + @testing.numpy_cupy_array_equal() + def test_array_f_contiguous_output(self, xp, dtype): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + b = xp.array(a, copy=False, order='F') + self.assertTrue(b.flags.f_contiguous) + return b + @testing.multi_gpu(2) def test_array_multi_device(self): with cuda.Device(0): From 93cdee00e7c0834de17ddd1405c4758aabfa215e Mon Sep 17 00:00:00 2001 From: Ryosuke Okuta Date: Thu, 27 Jul 2017 01:27:27 +0900 Subject: [PATCH 2/2] Fix contiguous check --- cupy/core/core.pyx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cupy/core/core.pyx b/cupy/core/core.pyx index 276dc8c03d6..4aa5c8eff48 100644 --- a/cupy/core/core.pyx +++ b/cupy/core/core.pyx @@ -1549,10 +1549,13 @@ cdef class ndarray: arr.dtype, self.dtype)) if self.shape != arr.shape: raise ValueError('Shape mismatch') - if not self._c_contiguous: + if self._c_contiguous: + arr = numpy.ascontiguousarray(arr) + elif self._f_contiguous: + arr = numpy.asfortranarray(arr) + else: raise RuntimeError('Cannot set to non-contiguous array') - arr = numpy.ascontiguousarray(arr) ptr = arr.ctypes.get_as_parameter() if stream is None: self.data.copy_from_host(ptr, self.nbytes)